<!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>[15048] CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest</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/15048">15048</a></dd>
<dt>Author</dt> <dd>sredmond@apple.com</dd>
<dt>Date</dt> <dd>2015-08-17 14:14:42 -0700 (Mon, 17 Aug 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Cleans up old files from trunk</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestrequestdataOS_X_10_7user_list_principal_property_searchrequest">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/request-data/OS_X_10_7/user_list_principal_property_search.request</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestdistributionspy">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/distributions.py</a></li>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadteststandardconfigscalendarsonlyplist">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/standard-configs/calendars-only.plist</a></li>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadteststandardconfigsclientsoldplist">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/standard-configs/clients-old.plist</a></li>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestteststest_distributionspy">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/tests/test_distributions.py</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestclientsplist">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/clients.plist</a></li>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestconfigdistplist">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/config.dist.plist</a></li>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestconfigplist">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/config.plist</a></li>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_icalpy">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_ical.py</a></li>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_populationpy">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_population.py</a></li>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_profilespy">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_profiles.py</a></li>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_simpy">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_sim.py</a></li>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_trafficloggerpy">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_trafficlogger.py</a></li>
<li><a href="#CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_webadminpy">CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_webadmin.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestclientsplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/clients.plist (15047 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/clients.plist        2015-08-17 20:38:08 UTC (rev 15047)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/clients.plist        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -1,548 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-
-&lt;!--
-    Copyright (c) 2011-2015 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-  --&gt;
-
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-        &lt;dict&gt;
-                &lt;!-- Define the kinds of software and user behavior the load simulation
-                        will simulate. --&gt;
-                &lt;key&gt;clients&lt;/key&gt;
-
-                &lt;!-- Have as many different kinds of software and user behavior configurations
-                        as you want. Each is a dict --&gt;
-                &lt;array&gt;
-
-                        &lt;dict&gt;
-
-                                &lt;!-- Here is a OS X client simulator. --&gt;
-                                &lt;key&gt;software&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_7&lt;/string&gt;
-
-                                &lt;!-- Arguments to use to initialize the OS_X_10_7 instance. --&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                        &lt;!-- Name that appears in logs. --&gt;
-                                        &lt;key&gt;title&lt;/key&gt;
-                                        &lt;string&gt;10.7&lt;/string&gt;
-        
-                                        &lt;!-- OS_X_10_7 can poll the calendar home at some interval. This is
-                                                in seconds. --&gt;
-                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
-                                        &lt;integer&gt;30&lt;/integer&gt;
-
-                                        &lt;!-- 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. --&gt;
-                                        &lt;key&gt;supportPush&lt;/key&gt;
-                                        &lt;false /&gt;
-
-                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
-                                        &lt;true/&gt;
-                                        &lt;key&gt;ampPushHost&lt;/key&gt;
-                                        &lt;string&gt;localhost&lt;/string&gt;
-                                        &lt;key&gt;ampPushPort&lt;/key&gt;
-                                        &lt;integer&gt;62311&lt;/integer&gt;
-                                &lt;/dict&gt;
-
-                                &lt;!-- The profiles define certain types of user behavior on top of the
-                                        client software being simulated. --&gt;
-                                &lt;key&gt;profiles&lt;/key&gt;
-                                &lt;array&gt;
-
-                                        &lt;!-- First an event-creating profile, which will periodically create
-                                                new events at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new event. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;60&lt;/integer&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
-                                                                        in the near future, limited to certain days of the week and certain hours
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;true/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile will create a new event, and then periodically update the ACKNOWLEDGED property. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.EventUpdater&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;false/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new event. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;5&lt;/integer&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
-                                                                        in the near future, limited to certain days of the week and certain hours
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;true/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;25&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;25&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;0&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;0&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;0&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;0&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;0&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
-                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.NormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mu gives the mean of the normal distribution (in seconds). --&gt;
-                                                                        &lt;key&gt;mu&lt;/key&gt;
-                                                                        &lt;integer&gt;60&lt;/integer&gt;
-
-                                                                        &lt;!-- and sigma gives its standard deviation. --&gt;
-                                                                        &lt;key&gt;sigma&lt;/key&gt;
-                                                                        &lt;integer&gt;5&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define the distribution of who will be invited to an event.
-                                                        
-                                                                When inviteeClumping is turned on each invitee is based on a sample of
-                                                                users &quot;close to&quot; the organizer based on account index. If the clumping
-                                                                is too &quot;tight&quot; for the requested number of attendees, then invites for
-                                                                those larger numbers will simply fail (the sim will report that situation).
-                                                                
-                                                                When inviteeClumping is off invitees will be sampled across an entire
-                                                                range of account indexes. In this case the distribution ought to be a
-                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
-                                                        --&gt;
-                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;min&lt;/key&gt;
-                                                                        &lt;integer&gt;0&lt;/integer&gt;
-                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
-                                                                        &lt;key&gt;max&lt;/key&gt;
-                                                                        &lt;integer&gt;99&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
-                                                        
-                                                                LogNormal is the best fit to observed data.
-
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
-                                                                mode should typically be 1, and mean whatever matches the user behavior.
-                                                                Our typical mean is 6.                                                         
-                                                             --&gt;
-                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mode - peak--&gt;
-                                                                        &lt;key&gt;mode&lt;/key&gt;
-                                                                        &lt;integer&gt;1&lt;/integer&gt;
-                                                                        &lt;!-- mean - average--&gt;
-                                                                        &lt;key&gt;median&lt;/key&gt;
-                                                                        &lt;integer&gt;6&lt;/integer&gt;
-                                                                        &lt;!-- maximum --&gt;
-                                                                        &lt;key&gt;maximum&lt;/key&gt;
-                                                                        &lt;real&gt;60&lt;/real&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
-                                                                        in the near future, limited to certain days of the week and certain hours
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-
-                                                        &lt;!-- Define how recurrences are created. --&gt;
-                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
-                                                                     RRULEs defined for this distribution and pick each based on a
-                                                                     weight. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- False to disable RRULEs --&gt;
-                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
-                                                                        &lt;true/&gt;
-
-                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
-                                                                        &lt;key&gt;weights&lt;/key&gt;
-                                                                        &lt;dict&gt;
-                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
-                                                                                &lt;key&gt;none&lt;/key&gt;
-                                                                                &lt;integer&gt;50&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
-                                                                                &lt;key&gt;daily&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                                &lt;key&gt;weekly&lt;/key&gt;
-                                                                                &lt;integer&gt;20&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
-                                                                                &lt;key&gt;monthly&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;yearly&lt;/key&gt;
-                                                                                &lt;integer&gt;1&lt;/integer&gt;
-                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;2&lt;/integer&gt;
-                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
-                                                                                &lt;integer&gt;5&lt;/integer&gt;
-                                                                                
-                                                                                &lt;!-- Work days pretty common --&gt;
-                                                                                &lt;key&gt;workdays&lt;/key&gt;
-                                                                                &lt;integer&gt;10&lt;/integer&gt;
-                                                                        &lt;/dict&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
-                                             handles replies received. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define how long to wait after seeing a new invitation before
-                                                                accepting it.
-
-                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
-                                                                (i.e., half of the user have accepted by that time).                                                                
-                                                        --&gt;
-                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- mode - peak--&gt;
-                                                                        &lt;key&gt;mode&lt;/key&gt;
-                                                                        &lt;integer&gt;300&lt;/integer&gt;
-                                                                        &lt;!-- median - 50% done--&gt;
-                                                                        &lt;key&gt;median&lt;/key&gt;
-                                                                        &lt;integer&gt;1800&lt;/integer&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                        &lt;!-- A task-creating profile, which will periodically create
-                                                new tasks at a random time on a random calendar. --&gt;
-                                        &lt;dict&gt;
-                                                &lt;key&gt;class&lt;/key&gt;
-                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
-
-                                                &lt;key&gt;params&lt;/key&gt;
-                                                &lt;dict&gt;
-                                                        &lt;key&gt;enabled&lt;/key&gt;
-                                                        &lt;true/&gt;
-
-                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
-                                                                its client to create a new task. --&gt;
-                                                        &lt;key&gt;interval&lt;/key&gt;
-                                                        &lt;integer&gt;300&lt;/integer&gt;
-
-                                                        &lt;!-- Define how due times (DUE) for the randomly generated tasks
-                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
-                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
-                                                        &lt;key&gt;taskDueDistribution&lt;/key&gt;
-                                                        &lt;dict&gt;
-
-                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
-                                                                        in the near future, limited to certain days of the week and certain hours
-                                                                        of the day. --&gt;
-                                                                &lt;key&gt;type&lt;/key&gt;
-                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
-
-                                                                &lt;key&gt;params&lt;/key&gt;
-                                                                &lt;dict&gt;
-                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
-                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
-                                                                        &lt;array&gt;
-                                                                                &lt;string&gt;mon&lt;/string&gt;
-                                                                                &lt;string&gt;tue&lt;/string&gt;
-                                                                                &lt;string&gt;wed&lt;/string&gt;
-                                                                                &lt;string&gt;thu&lt;/string&gt;
-                                                                                &lt;string&gt;fri&lt;/string&gt;
-                                                                        &lt;/array&gt;
-
-                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
-                                                                        &lt;key&gt;beginHour&lt;/key&gt;
-                                                                        &lt;integer&gt;8&lt;/integer&gt;
-
-                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
-                                                                                to begin!). --&gt;
-                                                                        &lt;key&gt;endHour&lt;/key&gt;
-                                                                        &lt;integer&gt;16&lt;/integer&gt;
-
-                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
-                                                                                really work right?) --&gt;
-                                                                        &lt;key&gt;tzname&lt;/key&gt;
-                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
-                                                                &lt;/dict&gt;
-                                                        &lt;/dict&gt;
-                                                &lt;/dict&gt;
-                                        &lt;/dict&gt;
-
-                                &lt;/array&gt;
-
-                                &lt;!-- Determine the frequency at which this client configuration will
-                                        appear in the clients which are created by the load tester. --&gt;
-                                &lt;key&gt;weight&lt;/key&gt;
-                                &lt;integer&gt;1&lt;/integer&gt;
-                        &lt;/dict&gt;
-                &lt;/array&gt;
-        &lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestconfigdistplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/config.dist.plist (15047 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/config.dist.plist        2015-08-17 20:38:08 UTC (rev 15047)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/config.dist.plist        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -1,188 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-
-&lt;!--
-    Copyright (c) 2011-2015 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-  --&gt;
-
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-        &lt;dict&gt;
-                &lt;!-- This is a distributed orchestrator configuration; 'workers' is a list of
-                                                        shell commands to run sub-processes.
-                                                        --&gt;
-                &lt;key&gt;workers&lt;/key&gt;
-                &lt;array&gt;
-                        &lt;string&gt;./bin/python contrib/performance/loadtest/ampsim.py&lt;/string&gt;
-                        &lt;string&gt;./bin/python contrib/performance/loadtest/ampsim.py&lt;/string&gt;
-                        &lt;string&gt;./bin/python contrib/performance/loadtest/ampsim.py&lt;/string&gt;
-                        &lt;string&gt;./bin/python contrib/performance/loadtest/ampsim.py&lt;/string&gt;
-                        &lt;string&gt;./bin/python contrib/performance/loadtest/ampsim.py&lt;/string&gt;
-                        &lt;string&gt;./bin/python contrib/performance/loadtest/ampsim.py&lt;/string&gt;
-                &lt;/array&gt;
-
-                &lt;!-- Identify the server to be load tested. --&gt;
-                &lt;key&gt;server&lt;/key&gt;
-                &lt;string&gt;https://127.0.0.1:8443&lt;/string&gt;
-
-                &lt;!-- The template URI for doing initial principal lookup on. --&gt;
-                &lt;key&gt;principalPathTemplate&lt;/key&gt;
-                &lt;string&gt;/principals/users/%s/&lt;/string&gt;
-
-                &lt;!-- Configure Admin Web UI. --&gt;
-                &lt;key&gt;webadmin&lt;/key&gt;
-                &lt;dict&gt;
-                        &lt;key&gt;enabled&lt;/key&gt;
-                        &lt;true/&gt;
-                        
-                        &lt;key&gt;HTTPPort&lt;/key&gt;
-                        &lt;integer&gt;8080&lt;/integer&gt;
-                &lt;/dict&gt;
-
-                &lt;!--  Define whether server supports stats socket. --&gt;
-                &lt;key&gt;serverStats&lt;/key&gt;
-                &lt;dict&gt;
-                        &lt;key&gt;enabled&lt;/key&gt;
-                        &lt;true/&gt;
-                        &lt;key&gt;Port&lt;/key&gt;
-                        &lt;integer&gt;8100&lt;/integer&gt;
-                &lt;/dict&gt;
-
-                &lt;!--  Define whether client data should be re-used. It will always be saved to the specified path.--&gt;
-                &lt;key&gt;clientDataSerialization&lt;/key&gt;
-                &lt;dict&gt;
-                        &lt;key&gt;UseOldData&lt;/key&gt;
-                        &lt;true/&gt;
-                        &lt;key&gt;Path&lt;/key&gt;
-                        &lt;string&gt;/tmp/sim&lt;/string&gt;
-                &lt;/dict&gt;
-
-                &lt;!-- Define the credentials of the clients which will be used to load test 
-                        the server. These credentials must already be valid on the server. --&gt;
-                &lt;key&gt;accounts&lt;/key&gt;
-                &lt;dict&gt;
-                        &lt;!-- The loader is the fully-qualified Python name of a callable which 
-                                returns a list of directory service records defining all of the client accounts 
-                                to use. contrib.performance.loadtest.sim.recordsFromCSVFile reads username, 
-                                password, mailto triples from a CSV file and returns them as a list of faked 
-                                directory service records. --&gt;
-                        &lt;key&gt;loader&lt;/key&gt;
-                        &lt;string&gt;contrib.performance.loadtest.sim.recordsFromCSVFile&lt;/string&gt;
-
-                        &lt;!-- Keyword arguments may be passed to the loader. --&gt;
-                        &lt;key&gt;params&lt;/key&gt;
-                        &lt;dict&gt;
-                                &lt;!-- recordsFromCSVFile interprets the path relative to the config.plist, 
-                                        to make it independent of the script's working directory while still allowing 
-                                        a relative path. This isn't a great solution. --&gt;
-                                &lt;key&gt;path&lt;/key&gt;
-                                &lt;string&gt;contrib/performance/loadtest/accounts.csv&lt;/string&gt;
-                        &lt;/dict&gt;
-                &lt;/dict&gt;
-
-                &lt;!-- Define how many clients will participate in the load test and how 
-                        they will show up. --&gt;
-                &lt;key&gt;arrival&lt;/key&gt;
-                &lt;dict&gt;
-
-                        &lt;!-- Specify a class which creates new clients and introduces them into 
-                                the test. contrib.performance.loadtest.population.SmoothRampUp introduces 
-                                groups of new clients at fixed intervals up to a maximum. The size of the 
-                                group, interval, and maximum are configured by the parameters below. The 
-                                total number of clients is groups * groupSize, which needs to be no larger 
-                                than the number of credentials created in the accounts section. --&gt;
-                        &lt;key&gt;factory&lt;/key&gt;
-                        &lt;string&gt;contrib.performance.loadtest.population.SmoothRampUp&lt;/string&gt;
-
-                        &lt;key&gt;params&lt;/key&gt;
-                        &lt;dict&gt;
-                                &lt;!-- groups gives the total number of groups of clients to introduce. --&gt;
-                                &lt;key&gt;groups&lt;/key&gt;
-                                &lt;integer&gt;99&lt;/integer&gt;
-
-                                &lt;!-- groupSize is the number of clients in each group of clients. It's 
-                                        really only a &quot;smooth&quot; ramp up if this is pretty small. --&gt;
-                                &lt;key&gt;groupSize&lt;/key&gt;
-                                &lt;integer&gt;1&lt;/integer&gt;
-
-                                &lt;!-- Number of seconds between the introduction of each group. --&gt;
-                                &lt;key&gt;interval&lt;/key&gt;
-                                &lt;integer&gt;3&lt;/integer&gt;
-
-                                &lt;!-- Number of clients each user is assigned to. --&gt;
-                                &lt;!-- Set weight of clients to 1 if this is &gt; 1. Number of clients must match this value if &gt; 1. --&gt;
-                                &lt;key&gt;clientsPerUser&lt;/key&gt;
-                                &lt;integer&gt;1&lt;/integer&gt;
-                        &lt;/dict&gt;
-
-                &lt;/dict&gt;
-
-                &lt;!-- Define some log observers to report on the load test. --&gt;
-                &lt;key&gt;observers&lt;/key&gt;
-                &lt;array&gt;
-                        &lt;!-- ReportStatistics generates an end-of-run summary of the HTTP requests 
-                                made, their timings, and their results. --&gt;
-                        &lt;dict&gt;
-                                &lt;key&gt;type&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.population.ReportStatistics&lt;/string&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                        &lt;!-- The thresholds for each request type --&gt;
-                                        &lt;key&gt;thresholdsPath&lt;/key&gt;
-                                        &lt;string&gt;contrib/performance/loadtest/thresholds.json&lt;/string&gt;
-                                        
-                                        &lt;!-- The benchmarks for overall QoS --&gt;
-                                        &lt;key&gt;benchmarksPath&lt;/key&gt;
-                                        &lt;string&gt;contrib/performance/loadtest/benchmarks.json&lt;/string&gt;
-
-                                        &lt;!-- The % of failures that constitute a failed test --&gt;
-                                        &lt;key&gt;failCutoff&lt;/key&gt;
-                                        &lt;real&gt;1.0&lt;/real&gt;
-                                &lt;/dict&gt;
-                        &lt;/dict&gt;
-        
-                        &lt;!-- RequestLogger generates a realtime log of all HTTP requests made 
-                                during the load test. --&gt;
-                        &lt;dict&gt;
-                                &lt;key&gt;type&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.ical.RequestLogger&lt;/string&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                &lt;/dict&gt;
-                        &lt;/dict&gt;
-        
-                        &lt;!-- OperationLogger generates an end-of-run summary of the gross operations 
-                                performed (logical operations which may span more than one HTTP request, 
-                                such as inviting an attendee to an event). --&gt;
-                        &lt;dict&gt;
-                                &lt;key&gt;type&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.profiles.OperationLogger&lt;/string&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                        &lt;!-- The thresholds for each operation type --&gt;
-                                        &lt;key&gt;thresholdsPath&lt;/key&gt;
-                                        &lt;string&gt;contrib/performance/loadtest/thresholds.json&lt;/string&gt;
-                                        
-                                        &lt;!-- The % of operations beyond the lag cut-off that constitute a failed test --&gt;
-                                        &lt;key&gt;lagCutoff&lt;/key&gt;
-                                        &lt;real&gt;1.0&lt;/real&gt;
-                                        
-                                        &lt;!-- The % of failures that constitute a failed test --&gt;
-                                        &lt;key&gt;failCutoff&lt;/key&gt;
-                                        &lt;real&gt;1.0&lt;/real&gt;
-                                &lt;/dict&gt;
-                        &lt;/dict&gt;
-                &lt;/array&gt;
-        &lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestconfigplist"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/config.plist (15047 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/config.plist        2015-08-17 20:38:08 UTC (rev 15047)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/config.plist        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -1,175 +0,0 @@
</span><del>-&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
-
-&lt;!--
-    Copyright (c) 2011-2015 Apple Inc. All rights reserved.
-
-    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-  --&gt;
-
-&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
-&lt;plist version=&quot;1.0&quot;&gt;
-        &lt;dict&gt;
-                &lt;!-- Identify the server to be load tested. --&gt;
-                &lt;key&gt;server&lt;/key&gt;
-                &lt;string&gt;https://127.0.0.1:8443&lt;/string&gt;
-
-                &lt;!-- The template URI for doing initial principal lookup on. --&gt;
-                &lt;key&gt;principalPathTemplate&lt;/key&gt;
-                &lt;string&gt;/principals/users/%s/&lt;/string&gt;
-
-                &lt;!-- Configure Admin Web UI. --&gt;
-                &lt;key&gt;webadmin&lt;/key&gt;
-                &lt;dict&gt;
-                        &lt;key&gt;enabled&lt;/key&gt;
-                        &lt;true/&gt;
-
-                        &lt;key&gt;HTTPPort&lt;/key&gt;
-                        &lt;integer&gt;8080&lt;/integer&gt;
-                &lt;/dict&gt;
-
-                &lt;!--  Define whether server supports stats socket. --&gt;
-                &lt;key&gt;serverStats&lt;/key&gt;
-                &lt;dict&gt;
-                        &lt;key&gt;enabled&lt;/key&gt;
-                        &lt;true/&gt;
-                        &lt;key&gt;Port&lt;/key&gt;
-                        &lt;integer&gt;8100&lt;/integer&gt;
-                &lt;/dict&gt;
-
-                &lt;!--  Define whether client data should be re-used. It will always be saved to the specified path.--&gt;
-                &lt;key&gt;clientDataSerialization&lt;/key&gt;
-                &lt;dict&gt;
-                        &lt;key&gt;UseOldData&lt;/key&gt;
-                        &lt;true/&gt;
-                        &lt;key&gt;Path&lt;/key&gt;
-                        &lt;string&gt;/tmp/sim&lt;/string&gt;
-                &lt;/dict&gt;
-
-                &lt;!-- Define the credentials of the clients which will be used to load test
-                        the server. These credentials must already be valid on the server. --&gt;
-                &lt;key&gt;accounts&lt;/key&gt;
-                &lt;dict&gt;
-                        &lt;!-- The loader is the fully-qualified Python name of a callable which
-                                returns a list of directory service records defining all of the client accounts
-                                to use. contrib.performance.loadtest.sim.recordsFromCSVFile reads username,
-                                password, mailto triples from a CSV file and returns them as a list of faked
-                                directory service records. --&gt;
-                        &lt;key&gt;loader&lt;/key&gt;
-                        &lt;string&gt;contrib.performance.loadtest.sim.recordsFromCSVFile&lt;/string&gt;
-
-                        &lt;!-- Keyword arguments may be passed to the loader. --&gt;
-                        &lt;key&gt;params&lt;/key&gt;
-                        &lt;dict&gt;
-                                &lt;!-- recordsFromCSVFile interprets the path relative to the config.plist,
-                                        to make it independent of the script's working directory while still allowing
-                                        a relative path. This isn't a great solution. --&gt;
-                                &lt;key&gt;path&lt;/key&gt;
-                                &lt;string&gt;contrib/performance/loadtest/accounts.csv&lt;/string&gt;
-                        &lt;/dict&gt;
-                &lt;/dict&gt;
-
-                &lt;!-- Define how many clients will participate in the load test and how
-                        they will show up. --&gt;
-                &lt;key&gt;arrival&lt;/key&gt;
-                &lt;dict&gt;
-
-                        &lt;!-- Specify a class which creates new clients and introduces them into
-                                the test. contrib.performance.loadtest.population.SmoothRampUp introduces
-                                groups of new clients at fixed intervals up to a maximum. The size of the
-                                group, interval, and maximum are configured by the parameters below. The
-                                total number of clients is groups * groupSize, which needs to be no larger
-                                than the number of credentials created in the accounts section. --&gt;
-                        &lt;key&gt;factory&lt;/key&gt;
-                        &lt;string&gt;contrib.performance.loadtest.population.SmoothRampUp&lt;/string&gt;
-
-                        &lt;key&gt;params&lt;/key&gt;
-                        &lt;dict&gt;
-                                &lt;!-- groups gives the total number of groups of clients to introduce. --&gt;
-                                &lt;key&gt;groups&lt;/key&gt;
-                                &lt;integer&gt;20&lt;/integer&gt;
-
-                                &lt;!-- groupSize is the number of clients in each group of clients. It's
-                                        really only a &quot;smooth&quot; ramp up if this is pretty small. --&gt;
-                                &lt;key&gt;groupSize&lt;/key&gt;
-                                &lt;integer&gt;1&lt;/integer&gt;
-
-                                &lt;!-- Number of seconds between the introduction of each group. --&gt;
-                                &lt;key&gt;interval&lt;/key&gt;
-                                &lt;integer&gt;3&lt;/integer&gt;
-
-                                &lt;!-- Number of clients each user is assigned to. --&gt;
-                                &lt;!-- Set weight of clients to 1 if this is &gt; 1. Number of clients must match this value if &gt; 1. --&gt;
-                                &lt;key&gt;clientsPerUser&lt;/key&gt;
-                                &lt;integer&gt;1&lt;/integer&gt;
-                        &lt;/dict&gt;
-
-                &lt;/dict&gt;
-
-                &lt;!-- Define some log observers to report on the load test. --&gt;
-                &lt;key&gt;observers&lt;/key&gt;
-                &lt;array&gt;
-                        &lt;!-- ReportStatistics generates an end-of-run summary of the HTTP requests 
-                                made, their timings, and their results. --&gt;
-                        &lt;dict&gt;
-                                &lt;key&gt;type&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.population.ReportStatistics&lt;/string&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                        &lt;!-- The thresholds for each request type --&gt;
-                                        &lt;key&gt;thresholdsPath&lt;/key&gt;
-                                        &lt;string&gt;contrib/performance/loadtest/thresholds.json&lt;/string&gt;
-                                        
-                                        &lt;!-- The benchmarks for overall QoS --&gt;
-                                        &lt;key&gt;benchmarksPath&lt;/key&gt;
-                                        &lt;string&gt;contrib/performance/loadtest/benchmarks.json&lt;/string&gt;
-
-                                        &lt;!-- The % of failures that constitute a failed test --&gt;
-                                        &lt;key&gt;failCutoff&lt;/key&gt;
-                                        &lt;real&gt;1.0&lt;/real&gt;
-                                &lt;/dict&gt;
-                        &lt;/dict&gt;
-        
-                        &lt;!-- RequestLogger generates a realtime log of all HTTP requests made 
-                                during the load test. --&gt;
-                        &lt;dict&gt;
-                                &lt;key&gt;type&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.ical.RequestLogger&lt;/string&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                &lt;/dict&gt;
-                        &lt;/dict&gt;
-        
-                        &lt;!-- OperationLogger generates an end-of-run summary of the gross operations 
-                                performed (logical operations which may span more than one HTTP request, 
-                                such as inviting an attendee to an event). --&gt;
-                        &lt;dict&gt;
-                                &lt;key&gt;type&lt;/key&gt;
-                                &lt;string&gt;contrib.performance.loadtest.profiles.OperationLogger&lt;/string&gt;
-                                &lt;key&gt;params&lt;/key&gt;
-                                &lt;dict&gt;
-                                        &lt;!-- The thresholds for each operation type --&gt;
-                                        &lt;key&gt;thresholdsPath&lt;/key&gt;
-                                        &lt;string&gt;contrib/performance/loadtest/thresholds.json&lt;/string&gt;
-                                        
-                                        &lt;!-- The % of operations beyond the lag cut-off that constitute a failed test --&gt;
-                                        &lt;key&gt;lagCutoff&lt;/key&gt;
-                                        &lt;real&gt;1.0&lt;/real&gt;
-                                        
-                                        &lt;!-- The % of failures that constitute a failed test --&gt;
-                                        &lt;key&gt;failCutoff&lt;/key&gt;
-                                        &lt;real&gt;1.0&lt;/real&gt;
-                                &lt;/dict&gt;
-                        &lt;/dict&gt;
-                &lt;/array&gt;
-        &lt;/dict&gt;
-&lt;/plist&gt;
</del></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestdistributionspy"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/distributions.py (0 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/distributions.py                                (rev 0)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/distributions.py        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -0,0 +1,432 @@
</span><ins>+##
+# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+&quot;&quot;&quot;
+Implementation of a statistics library for Calendar performance analysis.
+Exports:
+Base statistical functions
+  mean / median / stddev / mad / residuals
+
+IPopulation interface exposes:
+  sample()
+
+Sampling from this distribution must *not* change the underlying behavior of a distribution
+
+Distributions (all of which implement IPopulation):
+  UniformDiscreteDistribution
+  LogNormalDistribution
+  FixedDistribution
+  NearFutureDistribution
+  NormalDistribution
+  UniformIntegerDistribution
+  WorkDistribution
+  RecurrenceDistribution
+&quot;&quot;&quot;
+from math import log, sqrt
+from time import mktime
+import random
+import numpy.random as nprandom
+
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration as PyDuration
+from pycalendar.icalendar.property import Property
+from pycalendar.timezone import Timezone
+
+from zope.interface import Interface, implements
+from twisted.python.util import FancyEqMixin
+
+
+class IPopulation(Interface):
+    &quot;&quot;&quot;Interface for a class that provides a single function, `sample`, which returns a float&quot;&quot;&quot;
+    def sample(): #@NoSelf
+        pass
+
+
+class UniformDiscreteDistribution(object, FancyEqMixin):
+    &quot;&quot;&quot;
+
+    &quot;&quot;&quot;
+    implements(IPopulation)
+
+    compareAttributes = ['_values']
+
+    def __init__(self, values, randomize=True):
+        self._values = values
+        self._randomize = randomize
+        self._refill()
+
+
+    def _refill(self):
+        self._remaining = self._values[:]
+        if self._randomize:
+            random.shuffle(self._remaining)
+
+
+    def sample(self):
+        if not self._remaining:
+            self._refill()
+        return self._remaining.pop()
+
+
+
+class LogNormalDistribution(object, FancyEqMixin):
+    &quot;&quot;&quot;
+    &quot;&quot;&quot;
+    implements(IPopulation)
+
+    compareAttributes = ['_mu', '_sigma', '_maximum']
+
+    def __init__(self, mu=None, sigma=None, mean=None, mode=None, median=None, maximum=None):
+
+        if mu is not None and sigma is not None:
+            scale = 1.0
+        elif not (mu is None and sigma is None):
+            raise ValueError(&quot;mu and sigma must both be defined or both not defined&quot;)
+        elif mode is None:
+            raise ValueError(&quot;When mu and sigma are not defined, mode must be defined&quot;)
+        elif median is not None:
+            scale = mode
+            median /= mode
+            mode = 1.0
+            mu = log(median)
+            sigma = sqrt(log(median) - log(mode))
+        elif mean is not None:
+            scale = mode
+            mean /= mode
+            mode = 1.0
+            mu = log(mean) + log(mode) / 2.0
+            sigma = sqrt(log(mean) - log(mode) / 2.0)
+        else:
+            raise ValueError(&quot;When using mode one of median or mean must be defined&quot;)
+
+        self._mu = mu
+        self._sigma = sigma
+        self._scale = scale
+        self._maximum = maximum
+
+
+    def sample(self):
+        result = self._scale * random.lognormvariate(self._mu, self._sigma)
+        if self._maximum is not None and result &gt; self._maximum:
+            for _ignore_i in range(10):
+                result = self._scale * random.lognormvariate(self._mu, self._sigma)
+                if result &lt;= self._maximum:
+                    break
+            else:
+                raise ValueError(&quot;Unable to generate LogNormalDistribution sample within required range&quot;)
+        return result
+
+
+
+class FixedDistribution(object, FancyEqMixin):
+    &quot;&quot;&quot;
+    &quot;&quot;&quot;
+    implements(IPopulation)
+
+    compareAttributes = ['_value']
+
+    def __init__(self, value):
+        self._value = value
+
+
+    def sample(self):
+        return self._value
+
+
+
+class NearFutureDistribution(object, FancyEqMixin):
+    compareAttributes = ['_offset']
+
+    def __init__(self):
+        self._offset = LogNormalDistribution(7, 0.8)
+
+
+    def sample(self):
+        now = DateTime.getNowUTC()
+        now.offsetSeconds(int(self._offset.sample()))
+        return now
+
+
+
+class NormalDistribution(object, FancyEqMixin):
+    compareAttributes = ['_mu', '_sigma']
+
+    def __init__(self, mu, sigma):
+        self._mu = mu
+        self._sigma = sigma
+
+
+    def sample(self):
+        # Only return positive values or zero
+        v = random.normalvariate(self._mu, self._sigma)
+        while v &lt; 0:
+            v = random.normalvariate(self._mu, self._sigma)
+        return v
+
+
+
+class UniformIntegerDistribution(object, FancyEqMixin):
+    compareAttributes = ['_min', '_max']
+
+    def __init__(self, min, max):
+        self._min = min
+        self._max = max
+
+
+    def sample(self):
+        return int(random.uniform(self._min, self._max))
+
+
+class UniformRealDistribution(object, FancyEqMixin):
+    compareAttributes = ['_min', '_max']
+
+    def __init__(self, min, max):
+        self._min = min
+        self._max = max
+
+
+    def sample(self):
+        return random.uniform(self._min, self._max)
+
+
+class BernoulliDistribution(object, FancyEqMixin):
+    compareAttributes = [&quot;_p&quot;]
+
+    def __init__(self, proportion=0.5):
+        &quot;&quot;&quot;Initializes a bernoulli distribution with success probability given by p
+        Prereq: 0 &lt;= p &lt;= 1
+        Returns 1 with probability p, 0 with probability q = 1-p
+        &quot;&quot;&quot;
+        self._p = proportion
+
+    def sample(self):
+        return 1 if random.random() &lt;= self._p else 0
+
+
+
+class BinomialDistribution(object, FancyEqMixin):
+    compareAttributes = [&quot;_successProbability&quot;, &quot;_numTrials&quot;]
+
+    def __init__(self, p=0.5, n=10):
+        self._successProbability = p
+        self._numTrials = n
+
+    def sample(self):
+        return nprandom.binomial(self._numTrials, self._successProbability)
+
+
+
+class TriangularDistribution(object, FancyEqMixin):
+    compareAttributes = [&quot;_left&quot;, &quot;_mode&quot;, &quot;_right&quot;]
+
+    def __init__(self, left, mode, right):
+        self._left = left
+        self._mode = mode
+        self._right = right
+
+    def sample(self):
+        return nprandom.triangular(self._left, self._mode, self._right)
+
+
+class GeometricDistribution(object, FancyEqMixin):
+    &quot;&quot;&quot;
+    Expected number of Bernoulli trials before the first success
+    &quot;&quot;&quot;
+    compareAttributes = [&quot;_p&quot;]
+    def __init__(self, proportion=0.5):
+        self._p = proportion
+
+    def sample(self):
+        return nprandom.geometric(self._p)
+
+
+class PoissonDistribution(object, FancyEqMixin):
+    compareAttributes = [&quot;_lambda&quot;]
+    def __init__(self, lam):
+        self._lambda = lam
+
+    def sample(self):
+        return nprandom.possion(self._lambda)
+
+
+class BetaDistribution(object, FancyEqMixin):
+    compareAttributes = [&quot;_alpha&quot;, &quot;_beta&quot;]
+    def __init__(self, alpha, beta):
+        self._alpha = alpha
+        self._beta = beta
+
+    def sample(self):
+        return nprandom.beta(self._alpha, self._beta)
+
+
+class ChiSquaredDistribution(object, FancyEqMixin):
+    compareAttributes = [&quot;_df&quot;]
+    def __init__(self, degreesOfFreedom):
+        self._df = degreesOfFreedom
+
+    def sample(self):
+        return nprandom.chisquare(self._df)
+
+
+class ExponentialDistribution(object, FancyEqMixin):
+    compareAttributes = [&quot;_scale&quot;]
+    def __init__(self, scale):
+        self._scale = scale
+
+    def sample(self):
+        return nprandom.exponential(self._scale)
+
+
+
+class GammaDistribution(object, FancyEqMixin):
+    compareAttributes = [&quot;_shape&quot;, &quot;_scale&quot;]
+    def __init__(self, shape, scale=1.0):
+        self._shape = shape
+        self._scale = scale
+
+    def sample(self):
+        return nprandom.gamma(self._shape, self._scale)
+
+NUM_WEEKDAYS = 7
+
+class WorkDistribution(object, FancyEqMixin):
+    compareAttributes = [&quot;_daysOfWeek&quot;, &quot;_beginHour&quot;, &quot;_endHour&quot;]
+
+    _weekdayNames = [&quot;sun&quot;, &quot;mon&quot;, &quot;tue&quot;, &quot;wed&quot;, &quot;thu&quot;, &quot;fri&quot;, &quot;sat&quot;]
+
+    def __init__(self, daysOfWeek=[&quot;mon&quot;, &quot;tue&quot;, &quot;wed&quot;, &quot;thu&quot;, &quot;fri&quot;], beginHour=8, endHour=17, tzname=&quot;UTC&quot;):
+        self._daysOfWeek = [self._weekdayNames.index(day) for day in daysOfWeek]
+        self._beginHour = beginHour
+        self._endHour = endHour
+        self._tzname = tzname
+        self._helperDistribution = NormalDistribution(
+            # Mean 6 workdays in the future
+            60 * 60 * 8 * 6,
+            # Standard deviation of 4 workdays
+            60 * 60 * 8 * 4)
+        self.now = DateTime.getNow
+
+
+    def astimestamp(self, dt):
+        return mktime(dt.timetuple())
+
+
+    def _findWorkAfter(self, when):
+        &quot;&quot;&quot;
+        Return a two-tuple of the start and end of work hours following
+        C{when}.  If C{when} falls within work hours, then the start time will
+        be equal to when.
+        &quot;&quot;&quot;
+        # Find a workday that follows the timestamp
+        weekday = when.getDayOfWeek()
+        for i in range(NUM_WEEKDAYS):
+            day = when + PyDuration(days=i)
+            if (weekday + i) % NUM_WEEKDAYS in self._daysOfWeek:
+                # Joy, a day on which work might occur.  Find the first hour on
+                # this day when work may start.
+                day.setHHMMSS(self._beginHour, 0, 0)
+                begin = day
+                end = begin.duplicate()
+                end.setHHMMSS(self._endHour, 0, 0)
+                if end &gt; when:
+                    return begin, end
+
+
+    def sample(self):
+        offset = PyDuration(seconds=int(self._helperDistribution.sample()))
+        beginning = self.now(Timezone(tzid=self._tzname))
+        while offset:
+            start, end = self._findWorkAfter(beginning)
+            if end - start &gt; offset:
+                result = start + offset
+                result.setMinutes(result.getMinutes() // 15 * 15)
+                result.setSeconds(0)
+                return result
+            offset.setDuration(offset.getTotalSeconds() - (end - start).getTotalSeconds())
+            beginning = end
+
+
+
+class RecurrenceDistribution(object, FancyEqMixin):
+    compareAttributes = [&quot;_allowRecurrence&quot;, &quot;_weights&quot;]
+
+    _model_rrules = {
+        &quot;none&quot;: None,
+        &quot;daily&quot;: &quot;RRULE:FREQ=DAILY&quot;,
+        &quot;weekly&quot;: &quot;RRULE:FREQ=WEEKLY&quot;,
+        &quot;monthly&quot;: &quot;RRULE:FREQ=MONTHLY&quot;,
+        &quot;yearly&quot;: &quot;RRULE:FREQ=YEARLY&quot;,
+        &quot;dailylimit&quot;: &quot;RRULE:FREQ=DAILY;COUNT=14&quot;,
+        &quot;weeklylimit&quot;: &quot;RRULE:FREQ=WEEKLY;COUNT=4&quot;,
+        &quot;workdays&quot;: &quot;RRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR&quot;
+    }
+
+    def __init__(self, allowRecurrence, weights={}):
+        self._allowRecurrence = allowRecurrence
+        self._rrules = []
+        if self._allowRecurrence:
+            for rrule, count in sorted(weights.items(), key=lambda x: x[0]):
+                for _ignore in range(count):
+                    self._rrules.append(self._model_rrules[rrule])
+        self._helperDistribution = UniformIntegerDistribution(0, len(self._rrules) - 1)
+
+
+    def sample(self):
+
+        if self._allowRecurrence:
+            index = self._helperDistribution.sample()
+            rrule = self._rrules[index]
+            if rrule:
+                prop = Property.parseText(rrule)
+                return prop
+
+        return None
+
+class HappyFaceStartDistribution(object, FancyEqMixin):
+    compareAttributes = []
+    def __init__(self, scale):
+        self._scale = scale
+
+    def sample(self):
+        return nprandom.exponential(self._scale)
+
+class HappyFaceDurationDistribution(object, FancyEqMixin):
+    compareAttributes = []
+    def __init__(self):
+        self._distribution = UniformDiscreteDistribution(60 * 15, 60 * 30)
+
+    def sample(self):
+        return self._distribution.sample()
+
+
+if __name__ == '__main__':
+    from collections import defaultdict
+    mu = 1.5
+    sigma = 1.22
+    distribution = LogNormalDistribution(mu, sigma, 100)
+    result = defaultdict(int)
+    for i in range(100000):
+        s = int(distribution.sample())
+        if s &gt; 300:
+            continue
+        result[s] += 1
+
+    total = 0
+    for k, v in sorted(result.items(), key=lambda x: x[0]):
+        print(&quot;%d\t%.5f&quot; % (k, float(v) / result[1]))
+        total += k * v
+
+    print(&quot;Average: %.2f&quot; % (float(total) / sum(result.values()),))
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestrequestdataOS_X_10_7user_list_principal_property_searchrequest"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/request-data/OS_X_10_7/user_list_principal_property_search.request (15047 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/request-data/OS_X_10_7/user_list_principal_property_search.request        2015-08-17 20:38:08 UTC (rev 15047)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/request-data/OS_X_10_7/user_list_principal_property_search.request        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -1,2 +1,2 @@
</span><span class="cx"> &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
</span><del>-&lt;x0:principal-property-search xmlns:x2=&quot;urn:ietf:params:xml:ns:caldav&quot; xmlns:x0=&quot;DAV:&quot; xmlns:x1=&quot;http://calendarserver.org/ns/&quot; test=&quot;anyof&quot;&gt;&lt;x0:property-search&gt;&lt;x0:prop&gt;&lt;x0:displayname/&gt;&lt;/x0:prop&gt;&lt;x0:match match-type=&quot;starts-with&quot;&gt;%(displayname)s&lt;/x0:match&gt;&lt;/x0:property-search&gt;&lt;x0:property-search&gt;&lt;x0:prop&gt;&lt;x1:email-address-set/&gt;&lt;/x0:prop&gt;&lt;x0:match match-type=&quot;starts-with&quot;&gt;%(email)s&lt;/x0:match&gt;&lt;/x0:property-search&gt;&lt;x0:property-search&gt;&lt;x0:prop&gt;&lt;x1:first-name/&gt;&lt;/x0:prop&gt;&lt;x0:match match-type=&quot;starts-with&quot;&gt;%(firstname)s&lt;/x0:match&gt;&lt;/x0:property-search&gt;&lt;x0:property-search&gt;&lt;x0:prop&gt;&lt;x1:last-name/&gt;&lt;/x0:prop&gt;&lt;x0:match match-type=&quot;starts-with&quot;&gt;%(lastname)s&lt;/x0:match&gt;&lt;/x0:property-search&gt;&lt;x0:prop&gt;&lt;x1:email-address-se
 t/&gt;&lt;x2:calendar-user-address-set/&gt;&lt;x2:calendar-user-type/&gt;&lt;x0:displayname/&gt;&lt;x1:last-name/&gt;&lt;x1:first-name/&gt;&lt;x1:record-type/&gt;&lt;x0:principal-URL/&gt;&lt;/x0:prop&gt;&lt;/x0:principal-property-search&gt;
</del><span class="cx">\ No newline at end of file
</span><ins>+&lt;x0:principal-property-search xmlns:x2=&quot;urn:ietf:params:xml:ns:caldav&quot; xmlns:x0=&quot;DAV:&quot; xmlns:x1=&quot;http://calendarserver.org/ns/&quot; test=&quot;anyof&quot;&gt;&lt;x0:property-search&gt;&lt;x0:prop&gt;&lt;x0:displayname/&gt;&lt;/x0:prop&gt;&lt;x0:match match-type=&quot;starts-with&quot;&gt;%(displayname)s&lt;/x0:match&gt;&lt;/x0:property-search&gt;&lt;x0:property-search&gt;&lt;x0:prop&gt;&lt;x1:email-address-set/&gt;&lt;/x0:prop&gt;&lt;x0:match match-type=&quot;starts-with&quot;&gt;%(email)s&lt;/x0:match&gt;&lt;/x0:property-search&gt;&lt;x0:property-search&gt;&lt;x0:prop&gt;&lt;x1:first-name/&gt;&lt;/x0:prop&gt;&lt;x0:match match-type=&quot;starts-with&quot;&gt;%(firstname)s&lt;/x0:match&gt;&lt;/x0:property-search&gt;&lt;x0:property-search&gt;&lt;x0:prop&gt;&lt;x1:last-name/&gt;&lt;/x0:prop&gt;&lt;x0:match match-type=&quot;starts-with&quot;&gt;%(lastname)s&lt;/x0:match&gt;&lt;/x0:property-search&gt;&lt;x0:prop&gt;&lt;x1:email-address-se
 t/&gt;&lt;x2:calendar-user-address-set/&gt;&lt;x2:calendar-user-type/&gt;&lt;x0:displayname/&gt;&lt;x1:last-name/&gt;&lt;x1:first-name/&gt;&lt;x1:record-type/&gt;&lt;x0:principal-URL/&gt;&lt;/x0:prop&gt;&lt;/x0:principal-property-search&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadteststandardconfigscalendarsonlyplist"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/standard-configs/calendars-only.plist (0 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/standard-configs/calendars-only.plist                                (rev 0)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/standard-configs/calendars-only.plist        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -0,0 +1,138 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+
+&lt;!--
+    Copyright (c) 2011-2015 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  --&gt;
+
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+        &lt;dict&gt;
+                &lt;!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. --&gt;
+                &lt;key&gt;clients&lt;/key&gt;
+
+                &lt;!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict --&gt;
+                &lt;array&gt;
+                        &lt;dict&gt;
+                                &lt;!-- Here is a El Capitan iCal simulator. --&gt;
+                                &lt;key&gt;software&lt;/key&gt;
+                                &lt;string&gt;contrib.performance.loadtest.ical.OS_X_10_11&lt;/string&gt;
+
+                                &lt;!-- Arguments to use to initialize the client instance. --&gt;
+                                &lt;key&gt;params&lt;/key&gt;
+                                &lt;dict&gt;
+                                        &lt;!-- Name that appears in logs. --&gt;
+                                        &lt;key&gt;title&lt;/key&gt;
+                                        &lt;string&gt;10.11&lt;/string&gt;
+
+                                        &lt;!-- Client can poll the calendar home at some interval. This is
+                                                in seconds. --&gt;
+                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
+                                        &lt;integer&gt;5&lt;/integer&gt;
+
+                                        &lt;!-- If the server advertises xmpp push, OS X 10.11 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. --&gt;
+                                        &lt;key&gt;supportPush&lt;/key&gt;
+                                        &lt;false /&gt;
+                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
+                                        &lt;true /&gt;
+                                &lt;/dict&gt;
+
+                                &lt;!-- The profiles define certain types of user behavior on top of the
+                                        client software being simulated. --&gt;
+                                &lt;key&gt;profiles&lt;/key&gt;
+                                &lt;array&gt;
+
+                                        &lt;!-- First an calendar-creating profile, which will periodically create a new calendar --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.CalendarMaker&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true /&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new calendar. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;15&lt;/integer&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile will create a new event, and then periodically change something about the event. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.CalendarUpdater&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true /&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to update an existing calendar. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;5&lt;/integer&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile randomly shares calendars. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.CalendarSharer&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false /&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to share an existing calendar. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;30&lt;/integer&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile randomly deletes calendars. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.CalendarDeleter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true /&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to delete an existing calendar. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;30&lt;/integer&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+                                &lt;/array&gt;
+
+                                &lt;!-- Determine the frequency at which this client configuration will
+                                        appear in the clients which are created by the load tester. --&gt;
+                                &lt;key&gt;weight&lt;/key&gt;
+                                &lt;integer&gt;1&lt;/integer&gt;
+                        &lt;/dict&gt;
+                &lt;/array&gt;
+        &lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadteststandardconfigsclientsoldplist"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/standard-configs/clients-old.plist (0 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/standard-configs/clients-old.plist                                (rev 0)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/standard-configs/clients-old.plist        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -0,0 +1,527 @@
</span><ins>+&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
+
+&lt;!--
+    Copyright (c) 2011-2015 Apple Inc. All rights reserved.
+
+    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  --&gt;
+
+&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
+&lt;plist version=&quot;1.0&quot;&gt;
+        &lt;dict&gt;
+                &lt;!-- Define the kinds of software and user behavior the load simulation
+                        will simulate. --&gt;
+                &lt;key&gt;clients&lt;/key&gt;
+
+                &lt;!-- Have as many different kinds of software and user behavior configurations
+                        as you want. Each is a dict --&gt;
+                &lt;array&gt;
+
+                        &lt;dict&gt;
+
+                                &lt;!-- Here is a OS X client simulator. --&gt;
+                                &lt;key&gt;software&lt;/key&gt;
+                                &lt;string&gt;contrib.performance.loadtest.clients.OS_X_10_7&lt;/string&gt;
+
+                                &lt;!-- Arguments to use to initialize the OS_X_10_7 instance. --&gt;
+                                &lt;key&gt;params&lt;/key&gt;
+                                &lt;dict&gt;
+                                        &lt;!-- Name that appears in logs. --&gt;
+                                        &lt;key&gt;title&lt;/key&gt;
+                                        &lt;string&gt;10.7&lt;/string&gt;
+        
+                                        &lt;!-- OS_X_10_7 can poll the calendar home at some interval. This is
+                                                in seconds. --&gt;
+                                        &lt;key&gt;calendarHomePollInterval&lt;/key&gt;
+                                        &lt;integer&gt;30&lt;/integer&gt;
+
+                                        &lt;!-- 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. --&gt;
+                                        &lt;key&gt;supportPush&lt;/key&gt;
+                                        &lt;false /&gt;
+
+                                        &lt;key&gt;supportAmpPush&lt;/key&gt;
+                                        &lt;true/&gt;
+                                        &lt;key&gt;ampPushHost&lt;/key&gt;
+                                        &lt;string&gt;localhost&lt;/string&gt;
+                                        &lt;key&gt;ampPushPort&lt;/key&gt;
+                                        &lt;integer&gt;62311&lt;/integer&gt;
+                                &lt;/dict&gt;
+
+                                &lt;!-- The profiles define certain types of user behavior on top of the
+                                        client software being simulated. --&gt;
+                                &lt;key&gt;profiles&lt;/key&gt;
+                                &lt;array&gt;
+
+                                        &lt;!-- First an event-creating profile, which will periodically create
+                                                new events at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Eventer&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new event. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;300&lt;/integer&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
+                                                                        in the near future, limited to certain days of the week and certain hours
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;true/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
+                                                                                &lt;key&gt;none&lt;/key&gt;
+                                                                                &lt;integer&gt;50&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                                &lt;key&gt;weekly&lt;/key&gt;
+                                                                                &lt;integer&gt;20&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
+                                                                                &lt;key&gt;monthly&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;yearly&lt;/key&gt;
+                                                                                &lt;integer&gt;1&lt;/integer&gt;
+                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;5&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Work days pretty common --&gt;
+                                                                                &lt;key&gt;workdays&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile will create a new event, and then periodically update the ACKNOWLEDGED property. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.EventUpdater&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;false/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new event. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;300&lt;/integer&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
+                                                                        in the near future, limited to certain days of the week and certain hours
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;true/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
+                                                                                &lt;key&gt;none&lt;/key&gt;
+                                                                                &lt;integer&gt;50&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;25&lt;/integer&gt;
+                                                                                &lt;key&gt;weekly&lt;/key&gt;
+                                                                                &lt;integer&gt;25&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
+                                                                                &lt;key&gt;monthly&lt;/key&gt;
+                                                                                &lt;integer&gt;0&lt;/integer&gt;
+                                                                                &lt;key&gt;yearly&lt;/key&gt;
+                                                                                &lt;integer&gt;0&lt;/integer&gt;
+                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;0&lt;/integer&gt;
+                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;0&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Work days pretty common --&gt;
+                                                                                &lt;key&gt;workdays&lt;/key&gt;
+                                                                                &lt;integer&gt;0&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile invites some number of new attendees to new events. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.RealisticInviter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the frequency at which new invitations will be sent out. --&gt;
+                                                        &lt;key&gt;sendInvitationDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.NormalDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- mu gives the mean of the normal distribution (in seconds). --&gt;
+                                                                        &lt;key&gt;mu&lt;/key&gt;
+                                                                        &lt;integer&gt;60&lt;/integer&gt;
+
+                                                                        &lt;!-- and sigma gives its standard deviation. --&gt;
+                                                                        &lt;key&gt;sigma&lt;/key&gt;
+                                                                        &lt;integer&gt;5&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define the distribution of who will be invited to an event.
+                                                        
+                                                                When inviteeClumping is turned on each invitee is based on a sample of
+                                                                users &quot;close to&quot; the organizer based on account index. If the clumping
+                                                                is too &quot;tight&quot; for the requested number of attendees, then invites for
+                                                                those larger numbers will simply fail (the sim will report that situation).
+                                                                
+                                                                When inviteeClumping is off invitees will be sampled across an entire
+                                                                range of account indexes. In this case the distribution ought to be a
+                                                                UniformIntegerDistribution with min=0 and max set to the number of accounts.
+                                                        --&gt;
+                                                        &lt;key&gt;inviteeDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.UniformIntegerDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- The minimum value (inclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;min&lt;/key&gt;
+                                                                        &lt;integer&gt;0&lt;/integer&gt;
+                                                                        &lt;!-- The maximum value (exclusive) of the uniform distribution. --&gt;
+                                                                        &lt;key&gt;max&lt;/key&gt;
+                                                                        &lt;integer&gt;99&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;key&gt;inviteeClumping&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the distribution of how many attendees will be invited to an event.
+                                                        
+                                                                LogNormal is the best fit to observed data.
+
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;mean&quot; is the mean value.        For invites,
+                                                                mode should typically be 1, and mean whatever matches the user behavior.
+                                                                Our typical mean is 6.                                                         
+                                                             --&gt;
+                                                        &lt;key&gt;inviteeCountDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- mode - peak--&gt;
+                                                                        &lt;key&gt;mode&lt;/key&gt;
+                                                                        &lt;integer&gt;1&lt;/integer&gt;
+                                                                        &lt;!-- mean - average--&gt;
+                                                                        &lt;key&gt;median&lt;/key&gt;
+                                                                        &lt;integer&gt;6&lt;/integer&gt;
+                                                                        &lt;!-- maximum --&gt;
+                                                                        &lt;key&gt;maximum&lt;/key&gt;
+                                                                        &lt;real&gt;60&lt;/real&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how start times (DTSTART) for the randomly generated events
+                                                                will be selected. This is an example of a &quot;Distribution&quot; parameter. The value
+                                                                for most &quot;Distribution&quot; parameters are interchangeable and extensible. --&gt;
+                                                        &lt;key&gt;eventStartDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized. It produces timestamps
+                                                                        in the near future, limited to certain days of the week and certain hours
+                                                                        of the day. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.WorkDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- These are the days of the week the distribution will use. --&gt;
+                                                                        &lt;key&gt;daysOfWeek&lt;/key&gt;
+                                                                        &lt;array&gt;
+                                                                                &lt;string&gt;mon&lt;/string&gt;
+                                                                                &lt;string&gt;tue&lt;/string&gt;
+                                                                                &lt;string&gt;wed&lt;/string&gt;
+                                                                                &lt;string&gt;thu&lt;/string&gt;
+                                                                                &lt;string&gt;fri&lt;/string&gt;
+                                                                        &lt;/array&gt;
+
+                                                                        &lt;!-- The earliest hour of a day at which an event might be scheduled. --&gt;
+                                                                        &lt;key&gt;beginHour&lt;/key&gt;
+                                                                        &lt;integer&gt;8&lt;/integer&gt;
+
+                                                                        &lt;!-- And the latest hour of a day (at which an event will be scheduled
+                                                                                to begin!). --&gt;
+                                                                        &lt;key&gt;endHour&lt;/key&gt;
+                                                                        &lt;integer&gt;16&lt;/integer&gt;
+
+                                                                        &lt;!-- The timezone in which the event is scheduled. (XXX Does this
+                                                                                really work right?) --&gt;
+                                                                        &lt;key&gt;tzname&lt;/key&gt;
+                                                                        &lt;string&gt;America/Los_Angeles&lt;/string&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+
+                                                        &lt;!-- Define how recurrences are created. --&gt;
+                                                        &lt;key&gt;recurrenceDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+
+                                                                &lt;!-- This distribution is pretty specialized.  We have a fixed set of
+                                                                     RRULEs defined for this distribution and pick each based on a
+                                                                     weight. --&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.RecurrenceDistribution&lt;/string&gt;
+
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- False to disable RRULEs --&gt;
+                                                                        &lt;key&gt;allowRecurrence&lt;/key&gt;
+                                                                        &lt;true/&gt;
+
+                                                                        &lt;!-- These are the weights for the specific set of RRULEs. --&gt;
+                                                                        &lt;key&gt;weights&lt;/key&gt;
+                                                                        &lt;dict&gt;
+                                                                                &lt;!-- Half of all events will be non-recurring --&gt;
+                                                                                &lt;key&gt;none&lt;/key&gt;
+                                                                                &lt;integer&gt;50&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Daily and weekly are pretty common --&gt;
+                                                                                &lt;key&gt;daily&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                                &lt;key&gt;weekly&lt;/key&gt;
+                                                                                &lt;integer&gt;20&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Monthly, yearly, daily &amp; weekly limit not so common --&gt;
+                                                                                &lt;key&gt;monthly&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;yearly&lt;/key&gt;
+                                                                                &lt;integer&gt;1&lt;/integer&gt;
+                                                                                &lt;key&gt;dailylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;2&lt;/integer&gt;
+                                                                                &lt;key&gt;weeklylimit&lt;/key&gt;
+                                                                                &lt;integer&gt;5&lt;/integer&gt;
+                                                                                
+                                                                                &lt;!-- Work days pretty common --&gt;
+                                                                                &lt;key&gt;workdays&lt;/key&gt;
+                                                                                &lt;integer&gt;10&lt;/integer&gt;
+                                                                        &lt;/dict&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- This profile accepts invitations to events, handles cancels, and
+                                             handles replies received. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Accepter&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define how long to wait after seeing a new invitation before
+                                                                accepting it.
+
+                                                                For LogNormal &quot;mode&quot; is the peak, &quot;median&quot; is the 50% cummulative value
+                                                                (i.e., half of the user have accepted by that time).                                                                
+                                                        --&gt;
+                                                        &lt;key&gt;acceptDelayDistribution&lt;/key&gt;
+                                                        &lt;dict&gt;
+                                                                &lt;key&gt;type&lt;/key&gt;
+                                                                &lt;string&gt;contrib.performance.stats.LogNormalDistribution&lt;/string&gt;
+                                                                &lt;key&gt;params&lt;/key&gt;
+                                                                &lt;dict&gt;
+                                                                        &lt;!-- mode - peak--&gt;
+                                                                        &lt;key&gt;mode&lt;/key&gt;
+                                                                        &lt;integer&gt;300&lt;/integer&gt;
+                                                                        &lt;!-- median - 50% done--&gt;
+                                                                        &lt;key&gt;median&lt;/key&gt;
+                                                                        &lt;integer&gt;1800&lt;/integer&gt;
+                                                                &lt;/dict&gt;
+                                                        &lt;/dict&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- A task-creating profile, which will periodically create
+                                                new tasks at a random time on a random calendar. --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.Tasker&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new task. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;300&lt;/integer&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                        &lt;!-- A task-updating profile, which will periodically create
+                                                new tasks at a random time on a random calendar and then
+                                                update them in some way --&gt;
+                                        &lt;dict&gt;
+                                                &lt;key&gt;class&lt;/key&gt;
+                                                &lt;string&gt;contrib.performance.loadtest.profiles.TaskUpdater&lt;/string&gt;
+
+                                                &lt;key&gt;params&lt;/key&gt;
+                                                &lt;dict&gt;
+                                                        &lt;key&gt;enabled&lt;/key&gt;
+                                                        &lt;true/&gt;
+
+                                                        &lt;!-- Define the interval (in seconds) at which this profile will use
+                                                                its client to create a new task. --&gt;
+                                                        &lt;key&gt;interval&lt;/key&gt;
+                                                        &lt;integer&gt;60&lt;/integer&gt;
+                                                &lt;/dict&gt;
+                                        &lt;/dict&gt;
+
+                                &lt;/array&gt;
+
+                                &lt;!-- Determine the frequency at which this client configuration will
+                                        appear in the clients which are created by the load tester. --&gt;
+                                &lt;key&gt;weight&lt;/key&gt;
+                                &lt;integer&gt;1&lt;/integer&gt;
+                        &lt;/dict&gt;
+                &lt;/array&gt;
+        &lt;/dict&gt;
+&lt;/plist&gt;
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_icalpy"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_ical.py (15047 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_ical.py        2015-08-17 20:38:08 UTC (rev 15047)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_ical.py        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -1,2022 +0,0 @@
</span><del>-##
-# Copyright (c) 2010-2015 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-##
-
-from caldavclientlibrary.protocol.caldav.definitions import caldavxml
-from caldavclientlibrary.protocol.caldav.definitions import csxml
-from caldavclientlibrary.protocol.url import URL
-from caldavclientlibrary.protocol.webdav.definitions import davxml
-
-from contrib.performance.httpclient import MemoryConsumer, StringProducer
-from contrib.performance.loadtest.ical import XMPPPush, Event, Calendar, OS_X_10_6
-from contrib.performance.loadtest.sim import _DirectoryRecord
-
-from pycalendar.datetime import DateTime
-from pycalendar.timezone import Timezone
-
-from twisted.internet.defer import Deferred, inlineCallbacks, returnValue
-from twisted.internet.protocol import ProtocolToConsumerAdapter
-from twisted.python.failure import Failure
-from twisted.trial.unittest import TestCase
-from twisted.web.client import ResponseDone
-from twisted.web.http import OK, NO_CONTENT, CREATED, MULTI_STATUS
-from twisted.web.http_headers import Headers
-
-from twistedcaldav.ical import Component
-from twistedcaldav.timezones import TimezoneCache
-
-import json
-import os
-
-EVENT_UID = 'D94F247D-7433-43AF-B84B-ADD684D023B0'
-
-EVENT = &quot;&quot;&quot;\
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-CALSCALE:GREGORIAN
-BEGIN:VEVENT
-CREATED:20101018T155454Z
-UID:%(UID)s
-DTEND;TZID=America/New_York:20101028T130000
-ATTENDEE;CN=&quot;User 03&quot;;CUTYPE=INDIVIDUAL;EMAIL=&quot;user03@example.com&quot;;PARTS
- TAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:user03@example.co
- m
-ATTENDEE;CN=&quot;User 01&quot;;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:user01@
- example.com
-TRANSP:OPAQUE
-SUMMARY:Attended Event
-DTSTART;TZID=America/New_York:20101028T120000
-DTSTAMP:20101018T155513Z
-ORGANIZER;CN=&quot;User 01&quot;:mailto:user01@example.com
-SEQUENCE:3
-END:VEVENT
-END:VCALENDAR
-&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;) % {'UID': EVENT_UID}
-
-EVENT_INVITE = &quot;&quot;&quot;\
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-CALSCALE:GREGORIAN
-BEGIN:VTIMEZONE
-TZID:America/New_York
-X-LIC-LOCATION:America/New_York
-BEGIN:STANDARD
-DTSTART:18831118T120358
-RDATE:18831118T120358
-TZNAME:EST
-TZOFFSETFROM:-045602
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19180331T020000
-RRULE:FREQ=YEARLY;UNTIL=19190330T070000Z;BYDAY=-1SU;BYMONTH=3
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19181027T020000
-RRULE:FREQ=YEARLY;UNTIL=19191026T060000Z;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:STANDARD
-DTSTART:19200101T000000
-RDATE:19200101T000000
-RDATE:19420101T000000
-RDATE:19460101T000000
-RDATE:19670101T000000
-TZNAME:EST
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19200328T020000
-RDATE:19200328T020000
-RDATE:19740106T020000
-RDATE:19750223T020000
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19201031T020000
-RDATE:19201031T020000
-RDATE:19450930T020000
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19210424T020000
-RRULE:FREQ=YEARLY;UNTIL=19410427T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19210925T020000
-RRULE:FREQ=YEARLY;UNTIL=19410928T060000Z;BYDAY=-1SU;BYMONTH=9
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19420209T020000
-RDATE:19420209T020000
-TZNAME:EWT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:19450814T190000
-RDATE:19450814T190000
-TZNAME:EPT
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:19460428T020000
-RRULE:FREQ=YEARLY;UNTIL=19660424T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19460929T020000
-RRULE:FREQ=YEARLY;UNTIL=19540926T060000Z;BYDAY=-1SU;BYMONTH=9
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:STANDARD
-DTSTART:19551030T020000
-RRULE:FREQ=YEARLY;UNTIL=19661030T060000Z;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19670430T020000
-RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19671029T020000
-RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19760425T020000
-RRULE:FREQ=YEARLY;UNTIL=19860427T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:19870405T020000
-RRULE:FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-CREATED:20101018T155454Z
-UID:%(UID)s
-DTEND;TZID=America/New_York:20101028T130000
-ATTENDEE;CN=&quot;User 02&quot;;CUTYPE=INDIVIDUAL;EMAIL=&quot;user02@example.com&quot;;PARTS
- TAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:user02@example.co
- m
-ATTENDEE;CN=&quot;User 03&quot;;CUTYPE=INDIVIDUAL;EMAIL=&quot;user03@example.com&quot;;PARTS
- TAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:user03@example.co
- m
-ATTENDEE;CN=&quot;User 01&quot;;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:urn:uuid:user01
-TRANSP:OPAQUE
-SUMMARY:Attended Event
-DTSTART;TZID=America/New_York:20101028T120000
-DTSTAMP:20101018T155513Z
-ORGANIZER;CN=&quot;User 01&quot;:urn:uuid:user01
-SEQUENCE:3
-END:VEVENT
-END:VCALENDAR
-&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;) % {'UID': EVENT_UID}
-
-EVENT_AND_TIMEZONE = &quot;&quot;&quot;\
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-CALSCALE:GREGORIAN
-BEGIN:VTIMEZONE
-TZID:America/New_York
-X-LIC-LOCATION:America/New_York
-BEGIN:STANDARD
-DTSTART:18831118T120358
-RDATE:18831118T120358
-TZNAME:EST
-TZOFFSETFROM:-045602
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19180331T020000
-RRULE:FREQ=YEARLY;UNTIL=19190330T070000Z;BYDAY=-1SU;BYMONTH=3
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19181027T020000
-RRULE:FREQ=YEARLY;UNTIL=19191026T060000Z;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:STANDARD
-DTSTART:19200101T000000
-RDATE:19200101T000000
-RDATE:19420101T000000
-RDATE:19460101T000000
-RDATE:19670101T000000
-TZNAME:EST
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19200328T020000
-RDATE:19200328T020000
-RDATE:19740106T020000
-RDATE:19750223T020000
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19201031T020000
-RDATE:19201031T020000
-RDATE:19450930T020000
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19210424T020000
-RRULE:FREQ=YEARLY;UNTIL=19410427T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19210925T020000
-RRULE:FREQ=YEARLY;UNTIL=19410928T060000Z;BYDAY=-1SU;BYMONTH=9
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19420209T020000
-RDATE:19420209T020000
-TZNAME:EWT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:19450814T190000
-RDATE:19450814T190000
-TZNAME:EPT
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:19460428T020000
-RRULE:FREQ=YEARLY;UNTIL=19660424T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19460929T020000
-RRULE:FREQ=YEARLY;UNTIL=19540926T060000Z;BYDAY=-1SU;BYMONTH=9
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:STANDARD
-DTSTART:19551030T020000
-RRULE:FREQ=YEARLY;UNTIL=19661030T060000Z;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19670430T020000
-RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19671029T020000
-RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19760425T020000
-RRULE:FREQ=YEARLY;UNTIL=19860427T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:19870405T020000
-RRULE:FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-CREATED:20101018T155454Z
-UID:%(UID)s
-DTEND;TZID=America/New_York:20101028T130000
-ATTENDEE;CN=&quot;User 03&quot;;CUTYPE=INDIVIDUAL;EMAIL=&quot;user03@example.com&quot;;PARTS
- TAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:user03@example.co
- m
-ATTENDEE;CN=&quot;User 01&quot;;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:user01@
- example.com
-TRANSP:OPAQUE
-SUMMARY:Attended Event
-DTSTART;TZID=America/New_York:20101028T120000
-DTSTAMP:20101018T155513Z
-ORGANIZER;CN=&quot;User 01&quot;:mailto:user01@example.com
-SEQUENCE:3
-END:VEVENT
-END:VCALENDAR
-&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;) % {'UID': EVENT_UID}
-
-
-
-class EventTests(TestCase):
-    &quot;&quot;&quot;
-    Tests for L{Event}.
-    &quot;&quot;&quot;
-    def test_uid(self):
-        &quot;&quot;&quot;
-        When the C{vevent} attribute of an L{Event} instance is set,
-        L{Event.getUID} returns the UID value from it.
-        &quot;&quot;&quot;
-        event = Event(None, u'/foo/bar', u'etag', Component.fromString(EVENT))
-        self.assertEquals(event.getUID(), EVENT_UID)
-
-
-    def test_withoutUID(self):
-        &quot;&quot;&quot;
-        When an L{Event} has a C{vevent} attribute set to C{None},
-        L{Event.getUID} returns C{None}.
-        &quot;&quot;&quot;
-        event = Event(None, u'/bar/baz', u'etag')
-        self.assertIdentical(event.getUID(), None)
-
-
-
-PRINCIPAL_PROPFIND_RESPONSE = &quot;&quot;&quot;\
-&lt;?xml version='1.0' encoding='UTF-8'?&gt;
-&lt;multistatus xmlns='DAV:'&gt;
-  &lt;response&gt;
-    &lt;href&gt;/principals/__uids__/user01/&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;principal-collection-set&gt;
-          &lt;href&gt;/principals/&lt;/href&gt;
-        &lt;/principal-collection-set&gt;
-        &lt;calendar-home-set xmlns='urn:ietf:params:xml:ns:caldav'&gt;
-          &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01&lt;/href&gt;
-        &lt;/calendar-home-set&gt;
-        &lt;calendar-user-address-set xmlns='urn:ietf:params:xml:ns:caldav'&gt;
-          &lt;href xmlns='DAV:'&gt;/principals/__uids__/user01/&lt;/href&gt;
-          &lt;href xmlns='DAV:'&gt;/principals/users/user01/&lt;/href&gt;
-        &lt;/calendar-user-address-set&gt;
-        &lt;schedule-inbox-URL xmlns='urn:ietf:params:xml:ns:caldav'&gt;
-          &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/inbox/&lt;/href&gt;
-        &lt;/schedule-inbox-URL&gt;
-        &lt;schedule-outbox-URL xmlns='urn:ietf:params:xml:ns:caldav'&gt;
-          &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/outbox/&lt;/href&gt;
-        &lt;/schedule-outbox-URL&gt;
-        &lt;dropbox-home-URL xmlns='http://calendarserver.org/ns/'&gt;
-          &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/dropbox/&lt;/href&gt;
-        &lt;/dropbox-home-URL&gt;
-        &lt;notification-URL xmlns='http://calendarserver.org/ns/'&gt;
-          &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/notification/&lt;/href&gt;
-        &lt;/notification-URL&gt;
-        &lt;displayname&gt;User 01&lt;/displayname&gt;
-        &lt;principal-URL&gt;
-          &lt;href&gt;/principals/__uids__/user01/&lt;/href&gt;
-        &lt;/principal-URL&gt;
-        &lt;supported-report-set&gt;
-          &lt;supported-report&gt;
-            &lt;report&gt;
-              &lt;acl-principal-prop-set/&gt;
-            &lt;/report&gt;
-          &lt;/supported-report&gt;
-          &lt;supported-report&gt;
-            &lt;report&gt;
-              &lt;principal-match/&gt;
-            &lt;/report&gt;
-          &lt;/supported-report&gt;
-          &lt;supported-report&gt;
-            &lt;report&gt;
-              &lt;principal-property-search/&gt;
-            &lt;/report&gt;
-          &lt;/supported-report&gt;
-          &lt;supported-report&gt;
-            &lt;report&gt;
-              &lt;expand-property/&gt;
-            &lt;/report&gt;
-          &lt;/supported-report&gt;
-        &lt;/supported-report-set&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;xmpp-uri xmlns='http://calendarserver.org/ns/'/&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 404 Not Found&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-&lt;/multistatus&gt;
-&quot;&quot;&quot;
-
-_CALENDAR_HOME_PROPFIND_RESPONSE_TEMPLATE = &quot;&quot;&quot;\
-&lt;?xml version='1.0' encoding='UTF-8'?&gt;
-&lt;multistatus xmlns='DAV:'&gt;
-  &lt;response&gt;
-    &lt;href&gt;/calendars/__uids__/user01/&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        %(xmpp)s
-        &lt;displayname&gt;User 01&lt;/displayname&gt;
-        &lt;resourcetype&gt;
-          &lt;collection/&gt;
-        &lt;/resourcetype&gt;
-        &lt;owner&gt;
-          &lt;href&gt;/principals/__uids__/user01/&lt;/href&gt;
-        &lt;/owner&gt;
-        &lt;quota-available-bytes&gt;104855434&lt;/quota-available-bytes&gt;
-        &lt;quota-used-bytes&gt;2166&lt;/quota-used-bytes&gt;
-        &lt;current-user-privilege-set&gt;
-          &lt;privilege&gt;
-            &lt;all/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-free-busy xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-properties/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-content/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;bind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unbind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unlock/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-current-user-privilege-set/&gt;
-          &lt;/privilege&gt;
-        &lt;/current-user-privilege-set&gt;
-        &lt;push-transports xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;pushkey xmlns='http://calendarserver.org/ns/'&gt;/Some/Unique/Value&lt;/pushkey&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;getctag xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;calendar-description xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-color xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;calendar-order xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;supported-calendar-component-set xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;schedule-calendar-transp xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;schedule-default-calendar-URL xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-timezone xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;source xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-alarms xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-attachments xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-todos xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;refreshrate xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;publish-url xmlns='http://calendarserver.org/ns/'/&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 404 Not Found&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-  &lt;response&gt;
-    &lt;href&gt;/calendars/__uids__/user01/notification/&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;displayname&gt;notification&lt;/displayname&gt;
-        &lt;resourcetype&gt;
-          &lt;collection/&gt;
-          &lt;notification xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;/resourcetype&gt;
-        &lt;owner&gt;
-          &lt;href&gt;/principals/__uids__/user01/&lt;/href&gt;
-        &lt;/owner&gt;
-        &lt;quota-available-bytes&gt;104855434&lt;/quota-available-bytes&gt;
-        &lt;quota-used-bytes&gt;2166&lt;/quota-used-bytes&gt;
-        &lt;current-user-privilege-set&gt;
-          &lt;privilege&gt;
-            &lt;all/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-free-busy xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-properties/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-content/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;bind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unbind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unlock/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-current-user-privilege-set/&gt;
-          &lt;/privilege&gt;
-        &lt;/current-user-privilege-set&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;xmpp-server xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;xmpp-uri xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;getctag xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;calendar-description xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-color xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;calendar-order xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;supported-calendar-component-set xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;schedule-calendar-transp xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;schedule-default-calendar-URL xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-timezone xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;source xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-alarms xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-attachments xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-todos xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;refreshrate xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;push-transports xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;pushkey xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;publish-url xmlns='http://calendarserver.org/ns/'/&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 404 Not Found&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-  &lt;response&gt;
-    &lt;href&gt;/calendars/__uids__/user01/dropbox/&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;resourcetype&gt;
-          &lt;collection/&gt;
-          &lt;dropbox-home xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;/resourcetype&gt;
-        &lt;owner&gt;
-          &lt;href&gt;/principals/__uids__/user01/&lt;/href&gt;
-        &lt;/owner&gt;
-        &lt;quota-available-bytes&gt;104855434&lt;/quota-available-bytes&gt;
-        &lt;quota-used-bytes&gt;2166&lt;/quota-used-bytes&gt;
-        &lt;current-user-privilege-set&gt;
-          &lt;privilege&gt;
-            &lt;all/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-free-busy xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-properties/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-content/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;bind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unbind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unlock/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-current-user-privilege-set/&gt;
-          &lt;/privilege&gt;
-        &lt;/current-user-privilege-set&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;xmpp-server xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;xmpp-uri xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;getctag xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;displayname/&gt;
-        &lt;calendar-description xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-color xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;calendar-order xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;supported-calendar-component-set xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;schedule-calendar-transp xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;schedule-default-calendar-URL xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-timezone xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;source xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-alarms xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-attachments xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-todos xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;refreshrate xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;push-transports xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;pushkey xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;publish-url xmlns='http://calendarserver.org/ns/'/&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 404 Not Found&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-  &lt;response&gt;
-    &lt;href&gt;/calendars/__uids__/user01/calendar/&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;getctag xmlns='http://calendarserver.org/ns/'&gt;c2696540-4c4c-4a31-adaf-c99630776828#3&lt;/getctag&gt;
-        &lt;displayname&gt;calendar&lt;/displayname&gt;
-        &lt;calendar-color xmlns='http://apple.com/ns/ical/'&gt;#0252D4FF&lt;/calendar-color&gt;
-        &lt;calendar-order xmlns='http://apple.com/ns/ical/'&gt;1&lt;/calendar-order&gt;
-        &lt;supported-calendar-component-set xmlns='urn:ietf:params:xml:ns:caldav'&gt;
-          &lt;comp name='VEVENT'/&gt;
-          &lt;comp name='VTODO'/&gt;
-          &lt;comp name='VTIMEZONE'/&gt;
-          &lt;comp name='VFREEBUSY'/&gt;
-        &lt;/supported-calendar-component-set&gt;
-        &lt;resourcetype&gt;
-          &lt;collection/&gt;
-          &lt;calendar xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;/resourcetype&gt;
-        &lt;owner&gt;
-          &lt;href&gt;/principals/__uids__/user01/&lt;/href&gt;
-        &lt;/owner&gt;
-        &lt;schedule-calendar-transp xmlns='urn:ietf:params:xml:ns:caldav'&gt;
-          &lt;opaque/&gt;
-        &lt;/schedule-calendar-transp&gt;
-        &lt;quota-available-bytes&gt;104855434&lt;/quota-available-bytes&gt;
-        &lt;quota-used-bytes&gt;2166&lt;/quota-used-bytes&gt;
-        &lt;calendar-timezone xmlns='urn:ietf:params:xml:ns:caldav'&gt;&lt;![CDATA[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
-END:VCALENDAR
-]]&gt;&lt;/calendar-timezone&gt;
-        &lt;current-user-privilege-set&gt;
-          &lt;privilege&gt;
-            &lt;all/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-free-busy xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-properties/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-content/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;bind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unbind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unlock/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-current-user-privilege-set/&gt;
-          &lt;/privilege&gt;
-        &lt;/current-user-privilege-set&gt;
-        &lt;pushkey xmlns='http://calendarserver.org/ns/'/&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;xmpp-server xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;xmpp-uri xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;calendar-description xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;schedule-default-calendar-URL xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;source xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-alarms xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-attachments xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-todos xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;refreshrate xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;push-transports xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;publish-url xmlns='http://calendarserver.org/ns/'/&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 404 Not Found&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-  &lt;response&gt;
-    &lt;href&gt;/calendars/__uids__/user01/outbox/&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;supported-calendar-component-set xmlns='urn:ietf:params:xml:ns:caldav'&gt;
-          &lt;comp name='VEVENT'/&gt;
-          &lt;comp name='VTODO'/&gt;
-          &lt;comp name='VTIMEZONE'/&gt;
-          &lt;comp name='VFREEBUSY'/&gt;
-        &lt;/supported-calendar-component-set&gt;
-        &lt;resourcetype&gt;
-          &lt;collection/&gt;
-          &lt;schedule-outbox xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;/resourcetype&gt;
-        &lt;owner&gt;
-          &lt;href&gt;/principals/__uids__/user01/&lt;/href&gt;
-        &lt;/owner&gt;
-        &lt;quota-available-bytes&gt;104855434&lt;/quota-available-bytes&gt;
-        &lt;quota-used-bytes&gt;2166&lt;/quota-used-bytes&gt;
-        &lt;current-user-privilege-set&gt;
-          &lt;privilege&gt;
-            &lt;all/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-properties/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-content/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;bind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unbind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unlock/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-current-user-privilege-set/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;schedule-send xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;schedule xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-free-busy xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-        &lt;/current-user-privilege-set&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;xmpp-server xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;xmpp-uri xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;getctag xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;displayname/&gt;
-        &lt;calendar-description xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-color xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;calendar-order xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;schedule-calendar-transp xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;schedule-default-calendar-URL xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-timezone xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;source xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-alarms xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-attachments xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-todos xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;refreshrate xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;push-transports xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;pushkey xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;publish-url xmlns='http://calendarserver.org/ns/'/&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 404 Not Found&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-  &lt;response&gt;
-    &lt;href&gt;/calendars/__uids__/user01/freebusy&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;resourcetype&gt;
-          &lt;free-busy-url xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;/resourcetype&gt;
-        &lt;owner&gt;
-          &lt;href&gt;/principals/__uids__/user01/&lt;/href&gt;
-        &lt;/owner&gt;
-        &lt;quota-available-bytes&gt;104855434&lt;/quota-available-bytes&gt;
-        &lt;quota-used-bytes&gt;2166&lt;/quota-used-bytes&gt;
-        &lt;current-user-privilege-set&gt;
-          &lt;privilege&gt;
-            &lt;read/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;schedule-deliver xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;schedule xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;all/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-properties/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-content/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;bind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unbind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unlock/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-current-user-privilege-set/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-free-busy xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-        &lt;/current-user-privilege-set&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;xmpp-server xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;xmpp-uri xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;getctag xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;displayname/&gt;
-        &lt;calendar-description xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-color xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;calendar-order xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;supported-calendar-component-set xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;schedule-calendar-transp xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;schedule-default-calendar-URL xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-timezone xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;source xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-alarms xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-attachments xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-todos xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;refreshrate xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;push-transports xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;pushkey xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;publish-url xmlns='http://calendarserver.org/ns/'/&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 404 Not Found&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-  &lt;response&gt;
-    &lt;href&gt;/calendars/__uids__/user01/inbox/&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;getctag xmlns='http://calendarserver.org/ns/'&gt;a483dab3-1391-445b-b1c3-5ae9dfc81c2f#0&lt;/getctag&gt;
-        &lt;displayname&gt;inbox&lt;/displayname&gt;
-        &lt;supported-calendar-component-set xmlns='urn:ietf:params:xml:ns:caldav'&gt;
-          &lt;comp name='VEVENT'/&gt;
-          &lt;comp name='VTODO'/&gt;
-          &lt;comp name='VTIMEZONE'/&gt;
-          &lt;comp name='VFREEBUSY'/&gt;
-        &lt;/supported-calendar-component-set&gt;
-        &lt;resourcetype&gt;
-          &lt;collection/&gt;
-          &lt;schedule-inbox xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;/resourcetype&gt;
-        &lt;owner&gt;
-          &lt;href&gt;/principals/__uids__/user01/&lt;/href&gt;
-        &lt;/owner&gt;
-        &lt;calendar-free-busy-set xmlns='urn:ietf:params:xml:ns:caldav'&gt;
-          &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar&lt;/href&gt;
-        &lt;/calendar-free-busy-set&gt;
-        &lt;schedule-default-calendar-URL xmlns='urn:ietf:params:xml:ns:caldav'&gt;
-          &lt;href xmlns='DAV:'&gt;/calendars/__uids__/user01/calendar&lt;/href&gt;
-        &lt;/schedule-default-calendar-URL&gt;
-        &lt;quota-available-bytes&gt;104855434&lt;/quota-available-bytes&gt;
-        &lt;quota-used-bytes&gt;2166&lt;/quota-used-bytes&gt;
-        &lt;current-user-privilege-set&gt;
-          &lt;privilege&gt;
-            &lt;schedule-deliver xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;schedule xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;all/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-properties/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-content/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;bind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unbind/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;unlock/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;write-acl/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-current-user-privilege-set/&gt;
-          &lt;/privilege&gt;
-          &lt;privilege&gt;
-            &lt;read-free-busy xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-          &lt;/privilege&gt;
-        &lt;/current-user-privilege-set&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;xmpp-server xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;xmpp-uri xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;calendar-description xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-color xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;calendar-order xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;schedule-calendar-transp xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;calendar-timezone xmlns='urn:ietf:params:xml:ns:caldav'/&gt;
-        &lt;source xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-alarms xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-attachments xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;subscribed-strip-todos xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;refreshrate xmlns='http://apple.com/ns/ical/'/&gt;
-        &lt;push-transports xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;pushkey xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;publish-url xmlns='http://calendarserver.org/ns/'/&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 404 Not Found&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-&lt;/multistatus&gt;
-&quot;&quot;&quot;
-
-CALENDAR_HOME_PROPFIND_RESPONSE = _CALENDAR_HOME_PROPFIND_RESPONSE_TEMPLATE % {
-    &quot;xmpp&quot;: &quot;&quot;&quot;\
-        &lt;xmpp-server xmlns='http://calendarserver.org/ns/'/&gt;
-        &lt;xmpp-uri xmlns='http://calendarserver.org/ns/'/&gt;&quot;&quot;&quot;,
-}
-
-CALENDAR_HOME_PROPFIND_RESPONSE_WITH_XMPP = _CALENDAR_HOME_PROPFIND_RESPONSE_TEMPLATE % {
-    &quot;xmpp&quot;: &quot;&quot;&quot;\
-        &lt;xmpp-server xmlns='http://calendarserver.org/ns/'&gt;xmpp.example.invalid:1952&lt;/xmpp-server&gt;
-        &lt;xmpp-uri xmlns='http://calendarserver.org/ns/'&gt;xmpp:pubsub.xmpp.example.invalid?pubsub;node=/CalDAV/another.example.invalid/user01/&lt;/xmpp-uri&gt;&quot;&quot;&quot;,
-}
-
-CALENDAR_HOME_PROPFIND_RESPONSE_XMPP_MISSING = _CALENDAR_HOME_PROPFIND_RESPONSE_TEMPLATE % {&quot;xmpp&quot;: &quot;&quot;}
-
-
-
-class MemoryResponse(object):
-    def __init__(self, version, code, phrase, headers, bodyProducer):
-        self.version = version
-        self.code = code
-        self.phrase = phrase
-        self.headers = headers
-        self.length = bodyProducer.length
-        self._bodyProducer = bodyProducer
-
-
-    def deliverBody(self, protocol):
-        protocol.makeConnection(self._bodyProducer)
-        d = self._bodyProducer.startProducing(ProtocolToConsumerAdapter(protocol))
-        d.addCallback(lambda ignored: protocol.connectionLost(Failure(ResponseDone())))
-
-
-
-class OS_X_10_6Mixin:
-    &quot;&quot;&quot;
-    Mixin for L{TestCase}s for L{OS_X_10_6}.
-    &quot;&quot;&quot;
-    def setUp(self):
-        TimezoneCache.create()
-        self.record = _DirectoryRecord(
-            u&quot;user91&quot;, u&quot;user91&quot;, u&quot;User 91&quot;, u&quot;user91@example.org&quot;, u&quot;user91&quot;,
-        )
-        serializePath = self.mktemp()
-        os.mkdir(serializePath)
-        self.client = OS_X_10_6(
-            None,
-            &quot;http://127.0.0.1&quot;,
-            &quot;/principals/users/%s/&quot;,
-            serializePath,
-            self.record,
-            None,
-        )
-
-
-    def interceptRequests(self):
-        requests = []
-        def request(*args, **kwargs):
-            result = Deferred()
-            requests.append((result, args))
-            return result
-        self.client._request = request
-        return requests
-
-
-
-class OS_X_10_6Tests(OS_X_10_6Mixin, TestCase):
-    &quot;&quot;&quot;
-    Tests for L{OS_X_10_6}.
-    &quot;&quot;&quot;
-    def test_parsePrincipalPROPFINDResponse(self):
-        &quot;&quot;&quot;
-        L{Principal._parsePROPFINDResponse} accepts an XML document
-        like the one in the response to a I{PROPFIND} request for
-        I{/principals/__uids__/&lt;uid&gt;/} and returns a C{PropFindResult}
-        representing the data from it.
-        &quot;&quot;&quot;
-        principals = self.client._parseMultiStatus(PRINCIPAL_PROPFIND_RESPONSE)
-        principal = principals['/principals/__uids__/user01/']
-        self.assertEquals(
-            principal.getHrefProperties(),
-            {
-                davxml.principal_collection_set: URL(path='/principals/'),
-                caldavxml.calendar_home_set: URL(path='/calendars/__uids__/user01'),
-                caldavxml.calendar_user_address_set: (
-                    URL(path='/principals/__uids__/user01/'),
-                    URL(path='/principals/users/user01/'),
-                ),
-                caldavxml.schedule_inbox_URL: URL(path='/calendars/__uids__/user01/inbox/'),
-                caldavxml.schedule_outbox_URL: URL(path='/calendars/__uids__/user01/outbox/'),
-                csxml.dropbox_home_URL: URL(path='/calendars/__uids__/user01/dropbox/'),
-                csxml.notification_URL: URL(path='/calendars/__uids__/user01/notification/'),
-                davxml.principal_URL: URL(path='/principals/__uids__/user01/'),
-            }
-        )
-        self.assertEquals(
-            principal.getTextProperties(),
-            {davxml.displayname: 'User 01'})
-
-#         self.assertEquals(
-#             principal.getSomething(),
-#             {SUPPORTED_REPORT_SET: (
-#                     '{DAV:}acl-principal-prop-set',
-#                     '{DAV:}principal-match',
-#                     '{DAV:}principal-property-search',
-#                     '{DAV:}expand-property',
-#                     )})
-
-
-    def test_extractCalendars(self):
-        &quot;&quot;&quot;
-        L{OS_X_10_6._extractCalendars} accepts a calendar home
-        PROPFIND response body and returns a list of calendar objects
-        constructed from the data extracted from the response.
-        &quot;&quot;&quot;
-        home = &quot;/calendars/__uids__/user01/&quot;
-        calendars = self.client._extractCalendars(
-            self.client._parseMultiStatus(CALENDAR_HOME_PROPFIND_RESPONSE), home)
-        calendars.sort(key=lambda cal: cal.resourceType)
-        calendar, inbox = calendars
-
-        self.assertEquals(calendar.resourceType, caldavxml.calendar)
-        self.assertEquals(calendar.name, &quot;calendar&quot;)
-        self.assertEquals(calendar.url, &quot;/calendars/__uids__/user01/calendar/&quot;)
-        self.assertEquals(calendar.changeToken, &quot;c2696540-4c4c-4a31-adaf-c99630776828#3&quot;)
-
-        self.assertEquals(inbox.resourceType, caldavxml.schedule_inbox)
-        self.assertEquals(inbox.name, &quot;inbox&quot;)
-        self.assertEquals(inbox.url, &quot;/calendars/__uids__/user01/inbox/&quot;)
-        self.assertEquals(inbox.changeToken, &quot;a483dab3-1391-445b-b1c3-5ae9dfc81c2f#0&quot;)
-
-        self.assertEqual({}, self.client.xmpp)
-
-
-    def test_extractCalendarsXMPP(self):
-        &quot;&quot;&quot;
-        If there is XMPP push information in a calendar home PROPFIND response,
-        L{OS_X_10_6._extractCalendars} finds it and records it.
-        &quot;&quot;&quot;
-        home = &quot;/calendars/__uids__/user01/&quot;
-        self.client._extractCalendars(
-            self.client._parseMultiStatus(CALENDAR_HOME_PROPFIND_RESPONSE_WITH_XMPP),
-            home
-        )
-        self.assertEqual({
-            home: XMPPPush(
-                &quot;xmpp.example.invalid:1952&quot;,
-                &quot;xmpp:pubsub.xmpp.example.invalid?pubsub;node=/CalDAV/another.example.invalid/user01/&quot;,
-                &quot;/Some/Unique/Value&quot;
-            )},
-            self.client.xmpp
-        )
-
-
-    def test_handleMissingXMPP(self):
-        home = &quot;/calendars/__uids__/user01/&quot;
-        self.client._extractCalendars(
-            self.client._parseMultiStatus(CALENDAR_HOME_PROPFIND_RESPONSE_XMPP_MISSING), home)
-        self.assertEqual({}, self.client.xmpp)
-
-
-    @inlineCallbacks
-    def test_changeEventAttendee(self):
-        &quot;&quot;&quot;
-        OS_X_10_6.changeEventAttendee removes one attendee from an
-        existing event and appends another.
-        &quot;&quot;&quot;
-        requests = self.interceptRequests()
-
-        vevent = Component.fromString(EVENT)
-        attendees = tuple(vevent.mainComponent().properties(&quot;ATTENDEE&quot;))
-        old = attendees[0]
-        new = old.duplicate()
-        new.setParameter('CN', 'Some Other Guy')
-        event = Event(self.client.serializeLocation(), u'/some/calendar/1234.ics', None, vevent)
-        self.client._events[event.url] = event
-        self.client.changeEventAttendee(event.url, old, new)
-
-        _ignore_result, req = requests.pop(0)
-
-        # iCal PUTs the new VCALENDAR object.
-        _ignore_expectedResponseCode, method, url, headers, body = req
-        self.assertEquals(method, 'PUT')
-        self.assertEquals(url, 'http://127.0.0.1' + event.url)
-        self.assertIsInstance(url, str)
-        self.assertEquals(headers.getRawHeaders('content-type'), ['text/calendar'])
-
-        consumer = MemoryConsumer()
-        yield body.startProducing(consumer)
-        vevent = Component.fromString(consumer.value())
-        attendees = tuple(vevent.mainComponent().properties(&quot;ATTENDEE&quot;))
-        self.assertEquals(len(attendees), 2)
-        self.assertEquals(attendees[0].parameterValue('CN'), 'User 01')
-        self.assertEquals(attendees[1].parameterValue('CN'), 'Some Other Guy')
-
-
-    def test_addEvent(self):
-        &quot;&quot;&quot;
-        L{OS_X_10_6.addEvent} PUTs the event passed to it to the
-        server and updates local state to reflect its existence.
-        &quot;&quot;&quot;
-        requests = self.interceptRequests()
-
-        calendar = Calendar(caldavxml.calendar, set(('VEVENT',)), u'calendar', u'/mumble/', None)
-        self.client._calendars[calendar.url] = calendar
-
-        vcalendar = Component.fromString(EVENT)
-        d = self.client.addEvent(u'/mumble/frotz.ics', vcalendar)
-
-        result, req = requests.pop(0)
-
-        # iCal PUTs the new VCALENDAR object.
-        expectedResponseCode, method, url, headers, body = req
-        self.assertEqual(expectedResponseCode, CREATED)
-        self.assertEqual(method, 'PUT')
-        self.assertEqual(url, 'http://127.0.0.1/mumble/frotz.ics')
-        self.assertIsInstance(url, str)
-        self.assertEqual(headers.getRawHeaders('content-type'), ['text/calendar'])
-
-        consumer = MemoryConsumer()
-        finished = body.startProducing(consumer)
-        def cbFinished(ignored):
-            self.assertEqual(
-                Component.fromString(consumer.value()),
-                Component.fromString(EVENT_AND_TIMEZONE))
-        finished.addCallback(cbFinished)
-
-        def requested(ignored):
-            response = MemoryResponse(
-                ('HTTP', '1', '1'), CREATED, &quot;Created&quot;, Headers({}),
-                StringProducer(&quot;&quot;))
-            result.callback(response)
-        finished.addCallback(requested)
-
-        return d
-
-
-    @inlineCallbacks
-    def test_addInvite(self):
-        &quot;&quot;&quot;
-        L{OS_X_10_6.addInvite} PUTs the event passed to it to the
-        server and updates local state to reflect its existence, but
-        it also does attendee auto-complete and free-busy checks before
-        the PUT.
-        &quot;&quot;&quot;
-
-        calendar = Calendar(caldavxml.calendar, set(('VEVENT',)), u'calendar', u'/mumble/', None)
-        self.client._calendars[calendar.url] = calendar
-
-        vcalendar = Component.fromString(EVENT_INVITE)
-
-        self.client.uuid = u'urn:uuid:user01'
-        self.client.email = u'mailto:user01@example.com'
-        self.client.principalCollection = &quot;/principals/&quot;
-        self.client.outbox = &quot;/calendars/__uids__/user01/outbox/&quot;
-
-        @inlineCallbacks
-        def _testReport(*args, **kwargs):
-            expectedResponseCode, method, url, headers, body = args
-            self.assertEqual(expectedResponseCode, (MULTI_STATUS,))
-            self.assertEqual(method, 'REPORT')
-            self.assertEqual(url, 'http://127.0.0.1/principals/')
-            self.assertIsInstance(url, str)
-            self.assertEqual(headers.getRawHeaders('content-type'), ['text/xml'])
-
-            consumer = MemoryConsumer()
-            yield body.startProducing(consumer)
-
-            response = MemoryResponse(
-                ('HTTP', '1', '1'), MULTI_STATUS, &quot;MultiStatus&quot;, Headers({}),
-                StringProducer(&quot;&lt;?xml version='1.0' encoding='UTF-8'?&gt;&lt;multistatus xmlns='DAV:' /&gt;&quot;))
-
-            returnValue(response)
-
-        @inlineCallbacks
-        def _testPost(*args, **kwargs):
-            expectedResponseCode, method, url, headers, body = args
-            self.assertEqual(expectedResponseCode, OK)
-            self.assertEqual(method, 'POST')
-            self.assertEqual(url, 'http://127.0.0.1/calendars/__uids__/user01/outbox/')
-            self.assertIsInstance(url, str)
-            self.assertEqual(headers.getRawHeaders('content-type'), ['text/calendar'])
-
-            consumer = MemoryConsumer()
-            yield body.startProducing(consumer)
-            self.assertNotEqual(consumer.value().find(kwargs[&quot;attendee&quot;]), -1)
-
-            response = MemoryResponse(
-                ('HTTP', '1', '1'), OK, &quot;OK&quot;, Headers({}),
-                StringProducer(&quot;&quot;))
-
-            returnValue(response)
-
-        def _testPost02(*args, **kwargs):
-            return _testPost(*args, attendee=&quot;ATTENDEE:mailto:user02@example.com&quot;, **kwargs)
-
-        def _testPost03(*args, **kwargs):
-            return _testPost(*args, attendee=&quot;ATTENDEE:mailto:user03@example.com&quot;, **kwargs)
-
-        @inlineCallbacks
-        def _testPut(*args, **kwargs):
-            expectedResponseCode, method, url, headers, body = args
-            self.assertEqual(expectedResponseCode, CREATED)
-            self.assertEqual(method, 'PUT')
-            self.assertEqual(url, 'http://127.0.0.1/mumble/frotz.ics')
-            self.assertIsInstance(url, str)
-            self.assertEqual(headers.getRawHeaders('content-type'), ['text/calendar'])
-
-            consumer = MemoryConsumer()
-            yield body.startProducing(consumer)
-            self.assertEqual(
-                Component.fromString(consumer.value()),
-                Component.fromString(EVENT_INVITE))
-
-            response = MemoryResponse(
-                ('HTTP', '1', '1'), CREATED, &quot;Created&quot;, Headers({}),
-                StringProducer(&quot;&quot;))
-
-            returnValue(response)
-
-        requests = [_testReport, _testPost02, _testReport, _testPost03, _testPut, ]
-
-        def _requestHandler(*args, **kwargs):
-            handler = requests.pop(0)
-            return handler(*args, **kwargs)
-        self.client._request = _requestHandler
-        yield self.client.addInvite('/mumble/frotz.ics', vcalendar)
-
-
-    def test_deleteEvent(self):
-        &quot;&quot;&quot;
-        L{OS_X_10_6.deleteEvent} DELETEs the event at the relative
-        URL passed to it and updates local state to reflect its
-        removal.
-        &quot;&quot;&quot;
-        requests = self.interceptRequests()
-
-        calendar = Calendar(caldavxml.calendar, set(('VEVENT',)), u'calendar', u'/foo/', None)
-        event = Event(None, calendar.url + u'bar.ics', None)
-        self.client._calendars[calendar.url] = calendar
-        self.client._setEvent(event.url, event)
-
-        d = self.client.deleteEvent(event.url)
-
-        result, req = requests.pop()
-
-        expectedResponseCode, method, url = req
-
-        self.assertEqual(expectedResponseCode, NO_CONTENT)
-        self.assertEqual(method, 'DELETE')
-        self.assertEqual(url, 'http://127.0.0.1' + event.url)
-        self.assertIsInstance(url, str)
-
-        self.assertNotIn(event.url, self.client._events)
-        self.assertNotIn(u'bar.ics', calendar.events)
-
-        response = MemoryResponse(
-            ('HTTP', '1', '1'), NO_CONTENT, &quot;No Content&quot;, None,
-            StringProducer(&quot;&quot;))
-        result.callback(response)
-        return d
-
-
-    def test_serialization(self):
-        &quot;&quot;&quot;
-        L{OS_X_10_6.serialize} properly generates a JSON document.
-        &quot;&quot;&quot;
-        clientPath = os.path.join(self.client.serializePath, &quot;user91-OS_X_10.6&quot;)
-        self.assertFalse(os.path.exists(clientPath))
-        indexPath = os.path.join(clientPath, &quot;index.json&quot;)
-        self.assertFalse(os.path.exists(indexPath))
-
-        cal1 = &quot;&quot;&quot;BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-BEGIN:VEVENT
-UID:004f8e41-b071-4b30-bb3b-6aada4adcc10
-DTSTART:20120817T113000
-DTEND:20120817T114500
-DTSTAMP:20120815T154420Z
-SEQUENCE:2
-SUMMARY:Simple event
-END:VEVENT
-END:VCALENDAR
-&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
-        cal2 = &quot;&quot;&quot;BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-METHOD:REQUEST
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-BEGIN:VEVENT
-UID:00a79cad-857b-418e-a54a-340b5686d747
-DTSTART:20120817T113000
-DTEND:20120817T114500
-DTSTAMP:20120815T154420Z
-SEQUENCE:2
-SUMMARY:Simple event
-END:VEVENT
-END:VCALENDAR
-&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
-        events = (
-            Event(self.client.serializeLocation(), u'/home/calendar/1.ics', u'123.123', Component.fromString(cal1)),
-            Event(self.client.serializeLocation(), u'/home/inbox/i1.ics', u'123.123', Component.fromString(cal2)),
-        )
-        self.client._events.update(dict([[event.url, event] for event in events]))
-
-        calendars = (
-            Calendar(str(caldavxml.calendar), set(('VEVENT',)), u'calendar', u'/home/calendar/', &quot;123&quot;),
-            Calendar(str(caldavxml.calendar), set(('VTODO',)), u'tasks', u'/home/tasks/', &quot;456&quot;),
-            Calendar(str(caldavxml.schedule_inbox), set(('VEVENT', &quot;VTODO&quot;,)), u'calendar', u'/home/inbox/', &quot;789&quot;),
-        )
-        self.client._calendars.update(dict([[calendar.url, calendar] for calendar in calendars]))
-        self.client._calendars[&quot;/home/calendar/&quot;].events[&quot;1.ics&quot;] = events[0]
-        self.client._calendars[&quot;/home/inbox/&quot;].events[&quot;i1.ics&quot;] = events[1]
-
-        self.client.serialize()
-        self.assertTrue(os.path.exists(clientPath))
-        self.assertTrue(os.path.exists(indexPath))
-        def _normDict(d):
-            return dict([(k, sorted(v, key=lambda x: x[&quot;changeToken&quot; if k == &quot;calendars&quot; else &quot;url&quot;]) if v else None,) for k, v in d.items()])
-        self.assertEqual(_normDict(json.loads(open(indexPath).read())), _normDict(json.loads(&quot;&quot;&quot;{
-  &quot;calendars&quot;: [
-    {
-      &quot;changeToken&quot;: &quot;123&quot;,
-      &quot;name&quot;: &quot;calendar&quot;,
-      &quot;resourceType&quot;: &quot;{urn:ietf:params:xml:ns:caldav}calendar&quot;,
-      &quot;componentTypes&quot;: [
-        &quot;VEVENT&quot;
-      ],
-      &quot;url&quot;: &quot;/home/calendar/&quot;,
-      &quot;events&quot;: [
-        &quot;1.ics&quot;
-      ]
-    },
-    {
-      &quot;changeToken&quot;: &quot;789&quot;,
-      &quot;name&quot;: &quot;calendar&quot;,
-      &quot;resourceType&quot;: &quot;{urn:ietf:params:xml:ns:caldav}schedule-inbox&quot;,
-      &quot;componentTypes&quot;: [
-        &quot;VEVENT&quot;,
-        &quot;VTODO&quot;
-      ],
-      &quot;url&quot;: &quot;/home/inbox/&quot;,
-      &quot;events&quot;: [
-        &quot;i1.ics&quot;
-      ]
-    },
-    {
-      &quot;changeToken&quot;: &quot;456&quot;,
-      &quot;name&quot;: &quot;tasks&quot;,
-      &quot;resourceType&quot;: &quot;{urn:ietf:params:xml:ns:caldav}calendar&quot;,
-      &quot;componentTypes&quot;: [
-        &quot;VTODO&quot;
-      ],
-      &quot;url&quot;: &quot;/home/tasks/&quot;,
-      &quot;events&quot;: []
-    }
-  ],
-  &quot;principalURL&quot;: null,
-  &quot;events&quot;: [
-    {
-      &quot;url&quot;: &quot;/home/calendar/1.ics&quot;,
-      &quot;scheduleTag&quot;: null,
-      &quot;etag&quot;: &quot;123.123&quot;,
-      &quot;uid&quot;: &quot;004f8e41-b071-4b30-bb3b-6aada4adcc10&quot;
-    },
-    {
-      &quot;url&quot;: &quot;/home/inbox/i1.ics&quot;,
-      &quot;scheduleTag&quot;: null,
-      &quot;etag&quot;: &quot;123.123&quot;,
-      &quot;uid&quot;: &quot;00a79cad-857b-418e-a54a-340b5686d747&quot;
-    }
-  ]
-}&quot;&quot;&quot;)))
-
-        event1Path = os.path.join(clientPath, &quot;calendar&quot;, &quot;1.ics&quot;)
-        self.assertTrue(os.path.exists(event1Path))
-        self.assertEqual(open(event1Path).read(), cal1)
-
-        event2Path = os.path.join(clientPath, &quot;inbox&quot;, &quot;i1.ics&quot;)
-        self.assertTrue(os.path.exists(event2Path))
-        self.assertEqual(open(event2Path).read(), cal2)
-
-
-    def test_deserialization(self):
-        &quot;&quot;&quot;
-        L{OS_X_10_6.deserailize} properly parses a JSON document.
-        &quot;&quot;&quot;
-
-        cal1 = &quot;&quot;&quot;BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-BEGIN:VEVENT
-UID:004f8e41-b071-4b30-bb3b-6aada4adcc10
-DTSTART:20120817T113000
-DTEND:20120817T114500
-DTSTAMP:20120815T154420Z
-SEQUENCE:2
-SUMMARY:Simple event
-END:VEVENT
-END:VCALENDAR
-&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
-        cal2 = &quot;&quot;&quot;BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-METHOD:REQUEST
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-BEGIN:VEVENT
-UID:00a79cad-857b-418e-a54a-340b5686d747
-DTSTART:20120817T113000
-DTEND:20120817T114500
-DTSTAMP:20120815T154420Z
-SEQUENCE:2
-SUMMARY:Simple event
-END:VEVENT
-END:VCALENDAR
-&quot;&quot;&quot;.replace(&quot;\n&quot;, &quot;\r\n&quot;)
-
-        clientPath = os.path.join(self.client.serializePath, &quot;user91-OS_X_10.6&quot;)
-        os.mkdir(clientPath)
-        indexPath = os.path.join(clientPath, &quot;index.json&quot;)
-        open(indexPath, &quot;w&quot;).write(&quot;&quot;&quot;{
-  &quot;calendars&quot;: [
-    {
-      &quot;changeToken&quot;: &quot;321&quot;,
-      &quot;name&quot;: &quot;calendar&quot;,
-      &quot;resourceType&quot;: &quot;{urn:ietf:params:xml:ns:caldav}calendar&quot;,
-      &quot;componentTypes&quot;: [
-        &quot;VEVENT&quot;
-      ],
-      &quot;url&quot;: &quot;/home/calendar/&quot;,
-      &quot;events&quot;: [
-        &quot;2.ics&quot;
-      ]
-    },
-    {
-      &quot;changeToken&quot;: &quot;987&quot;,
-      &quot;name&quot;: &quot;calendar&quot;,
-      &quot;resourceType&quot;: &quot;{urn:ietf:params:xml:ns:caldav}schedule-inbox&quot;,
-      &quot;componentTypes&quot;: [
-        &quot;VEVENT&quot;,
-        &quot;VTODO&quot;
-      ],
-      &quot;url&quot;: &quot;/home/inbox/&quot;,
-      &quot;events&quot;: [
-        &quot;i2.ics&quot;
-      ]
-    },
-    {
-      &quot;changeToken&quot;: &quot;654&quot;,
-      &quot;name&quot;: &quot;tasks&quot;,
-      &quot;resourceType&quot;: &quot;{urn:ietf:params:xml:ns:caldav}calendar&quot;,
-      &quot;componentTypes&quot;: [
-        &quot;VTODO&quot;
-      ],
-      &quot;url&quot;: &quot;/home/tasks/&quot;,
-      &quot;events&quot;: []
-    }
-  ],
-  &quot;principalURL&quot;: null,
-  &quot;events&quot;: [
-    {
-      &quot;url&quot;: &quot;/home/calendar/2.ics&quot;,
-      &quot;scheduleTag&quot;: null,
-      &quot;etag&quot;: &quot;321.321&quot;,
-      &quot;uid&quot;: &quot;004f8e41-b071-4b30-bb3b-6aada4adcc10&quot;
-    },
-    {
-      &quot;url&quot;: &quot;/home/inbox/i2.ics&quot;,
-      &quot;scheduleTag&quot;: null,
-      &quot;etag&quot;: &quot;987.987&quot;,
-      &quot;uid&quot;: &quot;00a79cad-857b-418e-a54a-340b5686d747&quot;
-    }
-  ]
-}&quot;&quot;&quot;)
-
-        os.mkdir(os.path.join(clientPath, &quot;calendar&quot;))
-        event1Path = os.path.join(clientPath, &quot;calendar&quot;, &quot;2.ics&quot;)
-        open(event1Path, &quot;w&quot;).write(cal1)
-        os.mkdir(os.path.join(clientPath, &quot;inbox&quot;))
-        event1Path = os.path.join(clientPath, &quot;inbox&quot;, &quot;i2.ics&quot;)
-        open(event1Path, &quot;w&quot;).write(cal2)
-
-        self.client.deserialize()
-
-        self.assertEqual(len(self.client._calendars), 3)
-        self.assertTrue(&quot;/home/calendar/&quot; in self.client._calendars)
-        self.assertEqual(self.client._calendars[&quot;/home/calendar/&quot;].changeToken, &quot;321&quot;)
-        self.assertEqual(self.client._calendars[&quot;/home/calendar/&quot;].name, &quot;calendar&quot;)
-        self.assertEqual(self.client._calendars[&quot;/home/calendar/&quot;].resourceType, &quot;{urn:ietf:params:xml:ns:caldav}calendar&quot;)
-        self.assertEqual(self.client._calendars[&quot;/home/calendar/&quot;].componentTypes, set((&quot;VEVENT&quot;,)))
-        self.assertTrue(&quot;/home/tasks/&quot; in self.client._calendars)
-        self.assertTrue(&quot;/home/inbox/&quot; in self.client._calendars)
-        self.assertEqual(self.client._calendars[&quot;/home/inbox/&quot;].componentTypes, set((&quot;VEVENT&quot;, &quot;VTODO&quot;,)))
-        self.assertEqual(len(self.client._events), 2)
-        self.assertTrue(&quot;/home/calendar/2.ics&quot; in self.client._events)
-        self.assertEqual(self.client._events[&quot;/home/calendar/2.ics&quot;].scheduleTag, None)
-        self.assertEqual(self.client._events[&quot;/home/calendar/2.ics&quot;].etag, &quot;321.321&quot;)
-        self.assertEqual(self.client._events[&quot;/home/calendar/2.ics&quot;].getUID(), &quot;004f8e41-b071-4b30-bb3b-6aada4adcc10&quot;)
-        self.assertEqual(str(self.client._events[&quot;/home/calendar/2.ics&quot;].component), cal1)
-        self.assertTrue(&quot;/home/inbox/i2.ics&quot; in self.client._events)
-        self.assertEqual(self.client._events[&quot;/home/inbox/i2.ics&quot;].scheduleTag, None)
-        self.assertEqual(self.client._events[&quot;/home/inbox/i2.ics&quot;].etag, &quot;987.987&quot;)
-        self.assertEqual(self.client._events[&quot;/home/inbox/i2.ics&quot;].getUID(), &quot;00a79cad-857b-418e-a54a-340b5686d747&quot;)
-        self.assertEqual(str(self.client._events[&quot;/home/inbox/i2.ics&quot;].component), cal2)
-
-
-
-class UpdateCalendarTests(OS_X_10_6Mixin, TestCase):
-    &quot;&quot;&quot;
-    Tests for L{OS_X_10_6._updateCalendar}.
-    &quot;&quot;&quot;
-
-    _CALENDAR_PROPFIND_RESPONSE_BODY = &quot;&quot;&quot;\
-&lt;?xml version='1.0' encoding='UTF-8'?&gt;
-&lt;multistatus xmlns='DAV:'&gt;
-  &lt;response&gt;
-    &lt;href&gt;/something/anotherthing.ics&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;resourcetype&gt;
-          &lt;collection/&gt;
-        &lt;/resourcetype&gt;
-        &lt;getetag&gt;&quot;None&quot;&lt;/getetag&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 404 Not Found&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-  &lt;response&gt;
-    &lt;href&gt;/something/else.ics&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;resourcetype&gt;
-          &lt;collection/&gt;
-        &lt;/resourcetype&gt;
-        &lt;getetag&gt;&quot;None&quot;&lt;/getetag&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-   &lt;/response&gt;
-&lt;/multistatus&gt;
-&quot;&quot;&quot;
-    _CALENDAR_REPORT_RESPONSE_BODY = &quot;&quot;&quot;\
-&lt;?xml version='1.0' encoding='UTF-8'?&gt;
-&lt;multistatus xmlns='DAV:'&gt;
-  &lt;response&gt;
-    &lt;href&gt;/something/anotherthing.ics&lt;/href&gt;
-    &lt;status&gt;HTTP/1.1 404 Not Found&lt;/status&gt;
-  &lt;/response&gt;
-  &lt;response&gt;
-    &lt;href&gt;/something/else.ics&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;getetag&gt;&quot;ef70beb4cb7da4b2e2950350b09e9a01&quot;&lt;/getetag&gt;
-        &lt;calendar-data xmlns='urn:ietf:params:xml:ns:caldav'&gt;&lt;![CDATA[BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-BEGIN:VEVENT
-UID:CD54161A13AA8A4649D3781E@caldav.corp.apple.com
-DTSTART:20110715T140000Z
-DURATION:PT1H
-DTSTAMP:20110715T144217Z
-SUMMARY:Test2
-END:VEVENT
-END:VCALENDAR
-]]&gt;&lt;/calendar-data&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-&lt;/multistatus&gt;
-&quot;&quot;&quot;
-
-    _CALENDAR_REPORT_RESPONSE_BODY_1 = &quot;&quot;&quot;\
-&lt;?xml version='1.0' encoding='UTF-8'?&gt;
-&lt;multistatus xmlns='DAV:'&gt;
-  &lt;response&gt;
-    &lt;href&gt;/something/anotherthing.ics&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;getetag&gt;&quot;ef70beb4cb7da4b2e2950350b09e9a01&quot;&lt;/getetag&gt;
-        &lt;calendar-data xmlns='urn:ietf:params:xml:ns:caldav'&gt;&lt;![CDATA[BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-BEGIN:VEVENT
-UID:anotherthing@caldav.corp.apple.com
-DTSTART:20110715T140000Z
-DURATION:PT1H
-DTSTAMP:20110715T144217Z
-SUMMARY:Test1
-END:VEVENT
-END:VCALENDAR
-]]&gt;&lt;/calendar-data&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-&lt;/multistatus&gt;
-&quot;&quot;&quot;
-
-    _CALENDAR_REPORT_RESPONSE_BODY_2 = &quot;&quot;&quot;\
-&lt;?xml version='1.0' encoding='UTF-8'?&gt;
-&lt;multistatus xmlns='DAV:'&gt;
-  &lt;response&gt;
-    &lt;href&gt;/something/else.ics&lt;/href&gt;
-    &lt;propstat&gt;
-      &lt;prop&gt;
-        &lt;getetag&gt;&quot;ef70beb4cb7da4b2e2950350b09e9a01&quot;&lt;/getetag&gt;
-        &lt;calendar-data xmlns='urn:ietf:params:xml:ns:caldav'&gt;&lt;![CDATA[BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-BEGIN:VEVENT
-UID:else@caldav.corp.apple.com
-DTSTART:20110715T140000Z
-DURATION:PT1H
-DTSTAMP:20110715T144217Z
-SUMMARY:Test2
-END:VEVENT
-END:VCALENDAR
-]]&gt;&lt;/calendar-data&gt;
-      &lt;/prop&gt;
-      &lt;status&gt;HTTP/1.1 200 OK&lt;/status&gt;
-    &lt;/propstat&gt;
-  &lt;/response&gt;
-&lt;/multistatus&gt;
-&quot;&quot;&quot;
-
-    def test_eventMissing(self):
-        &quot;&quot;&quot;
-        If an event included in the calendar PROPFIND response no longer exists
-        by the time a REPORT is issued for that event, the 404 is handled and
-        the rest of the normal update logic for that event is skipped.
-        &quot;&quot;&quot;
-        requests = self.interceptRequests()
-
-        calendar = Calendar(None, set(('VEVENT',)), 'calendar', '/something/', None)
-        self.client._calendars[calendar.url] = calendar
-        self.client._updateCalendar(calendar, &quot;1234&quot;)
-        result, req = requests.pop(0)
-        expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
-        self.assertEqual('PROPFIND', method)
-        self.assertEqual('http://127.0.0.1/something/', url)
-        self.assertEqual((MULTI_STATUS,), expectedResponseCode)
-
-        result.callback(
-            MemoryResponse(
-                ('HTTP', '1', '1'), MULTI_STATUS, &quot;Multi-status&quot;, None,
-                StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)))
-
-        result, req = requests.pop(0)
-        expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
-        self.assertEqual('REPORT', method)
-        self.assertEqual('http://127.0.0.1/something/', url)
-        self.assertEqual((MULTI_STATUS,), expectedResponseCode)
-
-        # Someone else comes along and gets rid of the event
-        del self.client._events[&quot;/something/anotherthing.ics&quot;]
-
-        result.callback(
-            MemoryResponse(
-                ('HTTP', '1', '1'), MULTI_STATUS, &quot;Multi-status&quot;, None,
-                StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY)))
-
-        # Verify that processing proceeded to the response after the one with a
-        # 404 status.
-        self.assertIn('/something/else.ics', self.client._events)
-
-
-    def test_multigetBatch(self):
-        &quot;&quot;&quot;
-        If an event included in the calendar PROPFIND response no longer exists
-        by the time a REPORT is issued for that event, the 404 is handled and
-        the rest of the normal update logic for that event is skipped.
-        &quot;&quot;&quot;
-        requests = self.interceptRequests()
-
-        self.patch(self.client, &quot;MULTIGET_BATCH_SIZE&quot;, 1)
-
-        calendar = Calendar(None, set(('VEVENT',)), 'calendar', '/something/', None)
-        self.client._calendars[calendar.url] = calendar
-        self.client._updateCalendar(calendar, &quot;1234&quot;)
-        result, req = requests.pop(0)
-        expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
-        self.assertEqual('PROPFIND', method)
-        self.assertEqual('http://127.0.0.1/something/', url)
-        self.assertEqual((MULTI_STATUS,), expectedResponseCode)
-
-        result.callback(
-            MemoryResponse(
-                ('HTTP', '1', '1'), MULTI_STATUS, &quot;Multi-status&quot;, None,
-                StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)))
-
-        result, req = requests.pop(0)
-        expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
-        self.assertEqual('REPORT', method)
-        self.assertEqual('http://127.0.0.1/something/', url)
-        self.assertEqual((MULTI_STATUS,), expectedResponseCode)
-
-        result.callback(
-            MemoryResponse(
-                ('HTTP', '1', '1'), MULTI_STATUS, &quot;Multi-status&quot;, None,
-                StringProducer(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)
-
-        result, req = requests.pop(0)
-        expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
-        self.assertEqual('REPORT', method)
-        self.assertEqual('http://127.0.0.1/something/', url)
-        self.assertEqual((MULTI_STATUS,), expectedResponseCode)
-
-        result.callback(
-            MemoryResponse(
-                ('HTTP', '1', '1'), MULTI_STATUS, &quot;Multi-status&quot;, None,
-                StringProducer(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)
-
-
-
-class VFreeBusyTests(OS_X_10_6Mixin, TestCase):
-    &quot;&quot;&quot;
-    Tests for L{OS_X_10_6.requestAvailability}.
-    &quot;&quot;&quot;
-    def test_requestAvailability(self):
-        &quot;&quot;&quot;
-        L{OS_X_10_6.requestAvailability} accepts a date range and a set of
-        account uuids and issues a VFREEBUSY request.  It returns a Deferred
-        which fires with a dict mapping account uuids to availability range
-        information.
-        &quot;&quot;&quot;
-        self.client.uuid = u'urn:uuid:user01'
-        self.client.email = u'mailto:user01@example.com'
-        self.client.outbox = &quot;/calendars/__uids__/%s/outbox/&quot; % (self.record.uid,)
-        requests = self.interceptRequests()
-
-        start = DateTime(2011, 6, 10, 10, 45, 0, tzid=Timezone.UTCTimezone)
-        end = DateTime(2011, 6, 10, 11, 15, 0, tzid=Timezone.UTCTimezone)
-        d = self.client.requestAvailability(
-            start, end, [u&quot;urn:uuid:user05&quot;, u&quot;urn:uuid:user10&quot;])
-
-        result, req = requests.pop(0)
-        expectedResponseCode, method, url, headers, body = req
-
-        self.assertEqual(OK, expectedResponseCode)
-        self.assertEqual('POST', method)
-        self.assertEqual(
-            'http://127.0.0.1/calendars/__uids__/%s/outbox/' % (self.record.uid,),
-            url)
-
-        self.assertEqual(headers.getRawHeaders('originator'), ['mailto:user01@example.com'])
-        self.assertEqual(headers.getRawHeaders('recipient'), ['urn:uuid:user05, urn:uuid:user10'])
-        self.assertEqual(headers.getRawHeaders('content-type'), ['text/calendar'])
-
-        consumer = MemoryConsumer()
-        finished = body.startProducing(consumer)
-        def cbFinished(ignored):
-            vevent = Component.fromString(consumer.value())
-            uid = vevent.resourceUID()
-            dtstamp = vevent.mainComponent().propertyValue(&quot;DTSTAMP&quot;)
-            dtstamp = dtstamp.getText()
-            self.assertEqual(&quot;&quot;&quot;BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-VERSION:2.0
-METHOD:REQUEST
-PRODID:-//Apple Inc.//iCal 4.0.3//EN
-BEGIN:VFREEBUSY
-UID:%(uid)s
-DTEND:20110611T000000Z
-ATTENDEE:urn:uuid:user05
-ATTENDEE:urn:uuid:user10
-DTSTART:20110610T000000Z
-DTSTAMP:%(dtstamp)s
-ORGANIZER:mailto:user01@example.com
-SUMMARY:Availability for urn:uuid:user05, urn:uuid:user10
-END:VFREEBUSY
-END:VCALENDAR
-&quot;&quot;&quot;.replace('\n', '\r\n') % {'uid': uid, 'dtstamp': dtstamp}, consumer.value())
-
-        finished.addCallback(cbFinished)
-
-        def requested(ignored):
-            response = MemoryResponse(
-                ('HTTP', '1', '1'), OK, &quot;Ok&quot;, Headers({}),
-                StringProducer(&quot;&quot;))
-            result.callback(response)
-        finished.addCallback(requested)
-
-        return d
</del></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_populationpy"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_population.py (15047 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_population.py        2015-08-17 20:38:08 UTC (rev 15047)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_population.py        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -1,390 +0,0 @@
</span><del>-##
-# Copyright (c) 2011-2015 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-##
-
-&quot;&quot;&quot;
-Tests for some things in L{loadtest.population}.
-&quot;&quot;&quot;
-
-from twisted.trial.unittest import TestCase
-
-from contrib.performance.loadtest.population import ReportStatistics
-
-class ReportStatisticsTests(TestCase):
-    &quot;&quot;&quot;
-    Tests for L{loadtest.population.ReportStatistics}.
-    &quot;&quot;&quot;
-    def test_countUsers(self):
-        &quot;&quot;&quot;
-        L{ReportStatistics.countUsers} returns the number of users observed to
-        have acted in the simulation.
-        &quot;&quot;&quot;
-        logger = ReportStatistics()
-        users = ['user01', 'user02', 'user03']
-        for user in users:
-            logger.observe(dict(
-                type='response', method='GET', success=True,
-                duration=1.23, user=user, client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-            ))
-        self.assertEqual(len(users), logger.countUsers())
-
-
-    def test_countClients(self):
-        &quot;&quot;&quot;
-        L{ReportStatistics.countClients} returns the number of clients observed to
-        have acted in the simulation.
-        &quot;&quot;&quot;
-        logger = ReportStatistics()
-        clients = ['c01', 'c02', 'c03']
-        for client in clients:
-            logger.observe(dict(
-                type='response', method='GET', success=True,
-                duration=1.23, user=&quot;user01&quot;, client_type=&quot;test&quot;, client_id=client
-            ))
-        self.assertEqual(len(clients), logger.countClients())
-
-
-    def test_clientFailures(self):
-        &quot;&quot;&quot;
-        L{ReportStatistics.countClientFailures} returns the number of clients observed to
-        have failed in the simulation.
-        &quot;&quot;&quot;
-        logger = ReportStatistics()
-        clients = ['c01', 'c02', 'c03']
-        for client in clients:
-            logger.observe(dict(
-                type='client-failure', reason=&quot;testing %s&quot; % (client,)
-            ))
-        self.assertEqual(len(clients), logger.countClientFailures())
-
-
-    def test_simFailures(self):
-        &quot;&quot;&quot;
-        L{ReportStatistics.countSimFailures} returns the number of clients observed to
-        have caused an error in the simulation.
-        &quot;&quot;&quot;
-        logger = ReportStatistics()
-        clients = ['c01', 'c02', 'c03']
-        for client in clients:
-            logger.observe(dict(
-                type='sim-failure', reason=&quot;testing %s&quot; % (client,)
-            ))
-        self.assertEqual(len(clients), logger.countSimFailures())
-
-
-    def test_noFailures(self):
-        &quot;&quot;&quot;
-        If fewer than 1% of requests fail, fewer than 1% of requests take 5
-        seconds or more, and fewer than 5% of requests take 3 seconds or more,
-        L{ReportStatistics.failures} returns an empty list.
-        &quot;&quot;&quot;
-        logger = ReportStatistics()
-        logger.observe(dict(
-            type='response', method='GET', success=True,
-            duration=2.5, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        self.assertEqual([], logger.failures())
-
-
-    def test_requestFailures(self):
-        &quot;&quot;&quot;
-        If more than 1% of requests fail, L{ReportStatistics.failures} returns a
-        list containing a string describing this.
-        &quot;&quot;&quot;
-        logger = ReportStatistics()
-        for _ignore in range(98):
-            logger.observe(dict(
-                type='response', method='GET', success=True,
-                duration=2.5, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-            ))
-        logger.observe(dict(
-            type='response', method='GET', success=False,
-            duration=2.5, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        self.assertEqual(
-            [&quot;Greater than 1% GET failed&quot;],
-            logger.failures())
-
-
-    def test_threeSecondFailure(self):
-        &quot;&quot;&quot;
-        If more than 5% of requests take longer than 3 seconds,
-        L{ReportStatistics.failures} returns a list containing a string
-        describing that.
-        &quot;&quot;&quot;
-        logger = ReportStatistics()
-        for _ignore in range(94):
-            logger.observe(dict(
-                type='response', method='GET', success=True,
-                duration=2.5, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-            ))
-        for _ignore in range(5):
-            logger.observe(dict(
-                type='response', method='GET', success=True,
-                duration=3.5, user='user02', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-            ))
-        self.assertEqual(
-            [&quot;Greater than 5% GET exceeded 3 second response time&quot;],
-            logger.failures())
-
-
-    def test_fiveSecondFailure(self):
-        &quot;&quot;&quot;
-        If more than 1% of requests take longer than 5 seconds,
-        L{ReportStatistics.failures} returns a list containing a string
-        describing that.
-        &quot;&quot;&quot;
-        logger = ReportStatistics()
-        for _ignore in range(98):
-            logger.observe(dict(
-                type='response', method='GET', success=True,
-                duration=2.5, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-            ))
-        logger.observe(dict(
-            type='response', method='GET', success=True,
-            duration=5.5, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        self.assertEqual(
-            [&quot;Greater than 1% GET exceeded 5 second response time&quot;],
-            logger.failures())
-
-
-    def test_methodsCountedSeparately(self):
-        &quot;&quot;&quot;
-        The counts for one method do not affect the results of another method.
-        &quot;&quot;&quot;
-        logger = ReportStatistics()
-        for _ignore in range(99):
-            logger.observe(dict(
-                type='response', method='GET', success=True,
-                duration=2.5, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-            ))
-            logger.observe(dict(
-                type='response', method='POST', success=True,
-                duration=2.5, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-            ))
-
-        logger.observe(dict(
-            type='response', method='GET', success=False,
-            duration=2.5, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='POST', success=False,
-            duration=2.5, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-
-        self.assertEqual([], logger.failures())
-
-
-    def test_bucketRequest(self):
-        &quot;&quot;&quot;
-        PUT(xxx-huge/large/medium/small} have different thresholds. Test that requests straddling
-        each of those are correctly determined to be failures or not.
-        &quot;&quot;&quot;
-
-        _thresholds = {
-            &quot;requests&quot;: {
-                &quot;limits&quot;: [0.1, 0.5, 1.0, 3.0, 5.0, 10.0, 30.0],
-                &quot;thresholds&quot;: {
-                    &quot;default&quot;: [100.0, 100.0, 100.0, 5.0, 1.0, 0.5, 0.0],
-                    &quot;PUT{organizer-small}&quot;: [100.0, 50.0, 25.0, 5.0, 1.0, 0.5, 0.0],
-                    &quot;PUT{organizer-medium}&quot;: [100.0, 100.0, 50.0, 25.0, 5.0, 1.0, 0.5],
-                    &quot;PUT{organizer-large}&quot;: [100.0, 100.0, 100.0, 50.0, 25.0, 5.0, 1.0],
-                    &quot;PUT{organizer-huge}&quot;: [100.0, 100.0, 100.0, 100.0, 100.0, 50.0, 25.0],
-                }
-            }
-        }
-
-        # -small below threshold
-        logger = ReportStatistics(thresholds=_thresholds)
-        logger.observe(dict(
-            type='response', method='PUT{organizer-small}', success=True,
-            duration=0.2, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-small}', success=True,
-            duration=0.2, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-small}', success=True,
-            duration=0.2, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-small}', success=True,
-            duration=0.2, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        self.assertEqual([], logger.failures())
-
-        # -small above 0.5 threshold
-        logger = ReportStatistics(thresholds=_thresholds)
-        logger.observe(dict(
-            type='response', method='PUT{organizer-small}', success=True,
-            duration=0.2, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-small}', success=True,
-            duration=0.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-small}', success=True,
-            duration=0.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-small}', success=True,
-            duration=0.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        self.assertEqual(
-            [&quot;Greater than 50% PUT{organizer-small} exceeded 0.5 second response time&quot;],
-            logger.failures()
-        )
-
-        # -medium below 0.5 threshold
-        logger = ReportStatistics(thresholds=_thresholds)
-        logger.observe(dict(
-            type='response', method='PUT{organizer-medium}', success=True,
-            duration=0.2, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-medium}', success=True,
-            duration=0.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-medium}', success=True,
-            duration=0.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-medium}', success=True,
-            duration=0.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        self.assertEqual(
-            [],
-            logger.failures()
-        )
-
-        # -medium above 1.0 threshold
-        logger = ReportStatistics(thresholds=_thresholds)
-        logger.observe(dict(
-            type='response', method='PUT{organizer-medium}', success=True,
-            duration=0.2, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-medium}', success=True,
-            duration=1.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-medium}', success=True,
-            duration=1.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-medium}', success=True,
-            duration=1.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        self.assertEqual(
-            [&quot;Greater than 50% PUT{organizer-medium} exceeded 1 second response time&quot;],
-            logger.failures()
-        )
-
-        # -large below 1.0 threshold
-        logger = ReportStatistics(thresholds=_thresholds)
-        logger.observe(dict(
-            type='response', method='PUT{organizer-large}', success=True,
-            duration=0.2, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-large}', success=True,
-            duration=1.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-large}', success=True,
-            duration=1.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-large}', success=True,
-            duration=1.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        self.assertEqual(
-            [],
-            logger.failures()
-        )
-
-        # -large above 3.0 threshold
-        logger = ReportStatistics(thresholds=_thresholds)
-        logger.observe(dict(
-            type='response', method='PUT{organizer-large}', success=True,
-            duration=0.2, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-large}', success=True,
-            duration=3.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-large}', success=True,
-            duration=3.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-large}', success=True,
-            duration=3.6, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        self.assertEqual(
-            [&quot;Greater than 50% PUT{organizer-large} exceeded 3 second response time&quot;],
-            logger.failures()
-        )
-
-        # -huge below 10.0 threshold
-        logger = ReportStatistics(thresholds=_thresholds)
-        logger.observe(dict(
-            type='response', method='PUT{organizer-huge}', success=True,
-            duration=12.0, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-huge}', success=True,
-            duration=8, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-huge}', success=True,
-            duration=11.0, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-huge}', success=True,
-            duration=9.0, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        self.assertEqual(
-            [],
-            logger.failures()
-        )
-
-        # -huge above 10.0 threshold
-        logger = ReportStatistics(thresholds=_thresholds)
-        logger.observe(dict(
-            type='response', method='PUT{organizer-huge}', success=True,
-            duration=12.0, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-huge}', success=True,
-            duration=9.0, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-huge}', success=True,
-            duration=12.0, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        logger.observe(dict(
-            type='response', method='PUT{organizer-huge}', success=True,
-            duration=42.42, user='user01', client_type=&quot;test&quot;, client_id=&quot;1234&quot;
-        ))
-        self.assertEqual(
-            [&quot;Greater than 50% PUT{organizer-huge} exceeded 10 second response time&quot;],
-            logger.failures()
-        )
</del></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_profilespy"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_profiles.py (15047 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_profiles.py        2015-08-17 20:38:08 UTC (rev 15047)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_profiles.py        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -1,1091 +0,0 @@
</span><del>-##
-# Copyright (c) 2011-2015 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-##
-
-&quot;&quot;&quot;
-Tests for loadtest.profiles.
-&quot;&quot;&quot;
-
-from StringIO import StringIO
-
-from caldavclientlibrary.protocol.caldav.definitions import caldavxml, csxml
-
-from twisted.trial.unittest import TestCase
-from twisted.internet.task import Clock
-from twisted.internet.defer import succeed, fail
-from twisted.web.http import NO_CONTENT, PRECONDITION_FAILED
-from twisted.web.client import Response
-
-from twistedcaldav.ical import Component, Property
-
-from contrib.performance.loadtest.profiles import Eventer, Inviter, Accepter, OperationLogger
-from contrib.performance.loadtest.profiles import RealisticInviter
-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
-
-import os
-
-SIMPLE_EVENT = &quot;&quot;&quot;\
-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:20101018T155431Z
-UID:C98AD237-55AD-4F7D-9009-0D355D835822
-DTEND;TZID=America/New_York:20101021T130000
-TRANSP:OPAQUE
-SUMMARY:Simple event
-DTSTART;TZID=America/New_York:20101021T120000
-DTSTAMP:20101018T155438Z
-SEQUENCE:2
-END:VEVENT
-END:VCALENDAR
-&quot;&quot;&quot;
-
-INVITED_EVENT = &quot;&quot;&quot;\
-BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VTIMEZONE
-TZID:America/New_York
-BEGIN:STANDARD
-DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-END:VTIMEZONE
-BEGIN:VEVENT
-UID:882C3D50-0DAE-45CB-A2E7-DA75DA9BE452
-DTSTART;TZID=America/New_York:20110131T130000
-DTEND;TZID=America/New_York:20110131T140000
-ATTENDEE;CN=User 01;CUTYPE=INDIVIDUAL;EMAIL=user01@example.com;PARTSTAT=AC
- CEPTED:urn:uuid:user01
-ATTENDEE;CN=User 02;CUTYPE=INDIVIDUAL;EMAIL=user02@example.com;PARTSTAT=NE
- EDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:urn:uuid:user02
-CREATED:20110124T170357Z
-DTSTAMP:20110124T170425Z
-ORGANIZER;CN=User 01;EMAIL=user01@example.com:urn:uuid:user01
-SEQUENCE:3
-SUMMARY:Some Event For You
-TRANSP:TRANSPARENT
-X-APPLE-NEEDS-REPLY:TRUE
-END:VEVENT
-END:VCALENDAR
-&quot;&quot;&quot;
-
-ACCEPTED_EVENT = &quot;&quot;&quot;\
-BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VTIMEZONE
-TZID:America/New_York
-BEGIN:STANDARD
-DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-END:VTIMEZONE
-BEGIN:VEVENT
-UID:882C3D50-0DAE-45CB-A2E7-DA75DA9BE452
-DTSTART;TZID=America/New_York:20110131T130000
-DTEND;TZID=America/New_York:20110131T140000
-ATTENDEE;CN=User 01;CUTYPE=INDIVIDUAL;EMAIL=user01@example.com;PARTSTAT=AC
- CEPTED:urn:uuid:user01
-ATTENDEE;CN=User 02;CUTYPE=INDIVIDUAL;EMAIL=user02@example.com;PARTSTAT=AC
- CEPTED:urn:uuid:user02
-CREATED:20110124T170357Z
-DTSTAMP:20110124T170425Z
-ORGANIZER;CN=User 01;EMAIL=user01@example.com:urn:uuid:user01
-SEQUENCE:3
-SUMMARY:Some Event For You
-TRANSP:TRANSPARENT
-X-APPLE-NEEDS-REPLY:TRUE
-END:VEVENT
-END:VCALENDAR
-&quot;&quot;&quot;
-
-INBOX_REPLY = &quot;&quot;&quot;\
-BEGIN:VCALENDAR
-METHOD:REPLY
-VERSION:2.0
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VEVENT
-UID:12345-67890
-ORGANIZER;CN=&quot;User 01&quot;:mailto:user1@example.com
-ATTENDEE;PARTSTAT=ACCEPTED:mailto:user1@example.com
-END:VEVENT
-END:VCALENDAR
-&quot;&quot;&quot;
-
-
-
-class AnyUser(object):
-    def __getitem__(self, index):
-        return _AnyRecord(index)
-
-
-
-class _AnyRecord(object):
-    def __init__(self, index):
-        self.uid = u&quot;user%02d&quot; % (index,)
-        self.password = u&quot;user%02d&quot; % (index,)
-        self.commonName = u&quot;User %02d&quot; % (index,)
-        self.email = u&quot;user%02d@example.com&quot; % (index,)
-        self.guid = u&quot;user%02d&quot; % (index,)
-
-
-
-class Deterministic(object):
-    def __init__(self, value=None):
-        self.value = value
-
-
-    def gauss(self, mean, stddev):
-        &quot;&quot;&quot;
-        Pretend to return a value from a gaussian distribution with mu
-        parameter C{mean} and sigma parameter C{stddev}.  But actually
-        always return C{mean + 1}.
-        &quot;&quot;&quot;
-        return mean + 1
-
-
-    def choice(self, sequence):
-        return sequence[0]
-
-
-    def sample(self):
-        return self.value
-
-
-
-class StubClient(BaseClient):
-    &quot;&quot;&quot;
-    Stand in for an iCalendar client.
-
-    @ivar rescheduled: A set of event URLs which will not allow
-        attendee changes due to a changed schedule tag.
-    @ivar _pendingFailures: dict mapping URLs to failure objects
-    &quot;&quot;&quot;
-    def __init__(self, number, serializePath):
-        self.serializePath = serializePath
-        os.mkdir(self.serializePath)
-        self.title = &quot;StubClient&quot;
-        self._events = {}
-        self._calendars = {}
-        self._pendingFailures = {}
-        self.record = _DirectoryRecord(
-            &quot;user%02d&quot; % (number,), &quot;user%02d&quot; % (number,),
-            &quot;User %02d&quot; % (number,), &quot;user%02d@example.org&quot; % (number,),
-            &quot;user%02d&quot; % (number,))
-        self.email = &quot;mailto:user%02d@example.com&quot; % (number,)
-        self.uuid = &quot;urn:uuid:user%02d&quot; % (number,)
-        self.rescheduled = set()
-        self.started = True
-
-
-    def _failDeleteWithObject(self, href, failureObject):
-        &quot;&quot;&quot;
-        Accessor for inserting intentional failures for deletes.
-        &quot;&quot;&quot;
-        self._pendingFailures[href] = failureObject
-
-
-    def serializeLocation(self):
-        &quot;&quot;&quot;
-        Return the path to the directory where data for this user is serialized.
-        &quot;&quot;&quot;
-        if self.serializePath is None or not os.path.isdir(self.serializePath):
-            return None
-
-        key = &quot;%s-%s&quot; % (self.record.uid, &quot;StubClient&quot;)
-        path = os.path.join(self.serializePath, key)
-        if not os.path.exists(path):
-            os.mkdir(path)
-        elif not os.path.isdir(path):
-            return None
-
-        return path
-
-
-    def addEvent(self, href, vevent):
-        self._events[href] = Event(self.serializePath, href, None, vevent)
-        return succeed(None)
-
-
-    def addInvite(self, href, vevent):
-        return self.addEvent(href, vevent)
-
-
-    def deleteEvent(self, href,):
-        del self._events[href]
-        calendar, uid = href.rsplit('/', 1)
-        del self._calendars[calendar + '/'].events[uid]
-        if href in self._pendingFailures:
-            failureObject = self._pendingFailures.pop(href)
-            return fail(failureObject)
-        else:
-            return succeed(None)
-
-
-    def updateEvent(self, href):
-        self.rescheduled.remove(href)
-        return succeed(None)
-
-
-    def addEventAttendee(self, href, attendee):
-        vevent = self._events[href].component
-        vevent.mainComponent().addProperty(attendee)
-        self._events[href].component = vevent
-
-
-    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))
-            )
-
-        vevent = self._events[href].component
-        vevent.mainComponent().removeProperty(old)
-        vevent.mainComponent().addProperty(new)
-        self._events[href].component = vevent
-        return succeed(None)
-
-
-    def _makeSelfAttendee(self):
-        attendee = Property(
-            name=u'ATTENDEE',
-            value=self.email,
-            params={
-                'CN': self.record.commonName,
-                'CUTYPE': 'INDIVIDUAL',
-                'PARTSTAT': 'ACCEPTED',
-            },
-        )
-        return attendee
-
-
-    def _makeSelfOrganizer(self):
-        organizer = Property(
-            name=u'ORGANIZER',
-            value=self.email,
-            params={
-                'CN': self.record.commonName,
-            },
-        )
-        return organizer
-
-
-
-class SequentialDistribution(object):
-    def __init__(self, values):
-        self.values = values
-
-
-    def sample(self):
-        return self.values.pop(0)
-
-
-
-class InviterTests(TestCase):
-    &quot;&quot;&quot;
-    Tests for loadtest.profiles.Inviter.
-    &quot;&quot;&quot;
-    def setUp(self):
-        self.sim = CalendarClientSimulator(
-            AnyUser(), Populator(None), None, None, None, None, None)
-
-
-    def _simpleAccount(self, userNumber, eventText):
-        client = StubClient(userNumber, self.mktemp())
-
-        vevent = Component.fromString(eventText)
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'calendar', u'/cal/', None)
-        client._calendars.update({calendar.url: calendar})
-
-        event = Event(client.serializeLocation(), calendar.url + u'1234.ics', None, vevent)
-
-        client._events.update({event.url: event})
-        calendar.events = {u'1234.ics': event}
-
-        return vevent, event, calendar, client
-
-
-    def test_enabled(self):
-        userNumber = 13
-        client = StubClient(userNumber, self.mktemp())
-
-        inviter = Inviter(None, self.sim, client, userNumber, **{&quot;enabled&quot;: False})
-        self.assertEqual(inviter.enabled, False)
-
-        inviter = Inviter(None, self.sim, client, userNumber, **{&quot;enabled&quot;: True})
-        self.assertEqual(inviter.enabled, True)
-
-
-    def test_doNotAddAttendeeToInbox(self):
-        &quot;&quot;&quot;
-        When the only calendar with any events is a schedule inbox, no
-        attempt is made to add attendees to an event on that calendar.
-        &quot;&quot;&quot;
-        userNumber = 10
-        vevent, _ignore_event, calendar, client = self._simpleAccount(
-            userNumber, SIMPLE_EVENT)
-        calendar.resourceType = caldavxml.schedule_inbox
-        inviter = Inviter(None, self.sim, client, userNumber)
-        inviter._invite()
-        self.assertFalse(vevent.mainComponent().hasProperty('ATTENDEE'))
-
-
-    def test_doNotAddAttendeeToNoCalendars(self):
-        &quot;&quot;&quot;
-        When there are no calendars and no events at all, the inviter
-        does nothing.
-        &quot;&quot;&quot;
-        userNumber = 13
-        client = StubClient(userNumber, self.mktemp())
-        inviter = Inviter(None, self.sim, client, userNumber)
-        inviter._invite()
-        self.assertEquals(client._events, {})
-        self.assertEquals(client._calendars, {})
-
-
-    def test_doNotAddAttendeeToUninitializedEvent(self):
-        &quot;&quot;&quot;
-        When there is an L{Event} on a calendar but the details of the
-        event have not yet been retrieved, no attempt is made to add
-        invitees to that event.
-        &quot;&quot;&quot;
-        userNumber = 19
-        _ignore_vevent, event, calendar, client = self._simpleAccount(
-            userNumber, SIMPLE_EVENT)
-        event.component = event.etag = event.scheduleTag = None
-        inviter = Inviter(None, self.sim, client, userNumber)
-        inviter._invite()
-        self.assertEquals(client._events, {event.url: event})
-        self.assertEquals(client._calendars, {calendar.url: calendar})
-
-
-    def test_addAttendeeToEvent(self):
-        &quot;&quot;&quot;
-        When there is a normal calendar with an event, inviter adds an
-        attendee to it.
-        &quot;&quot;&quot;
-        userNumber = 16
-        _ignore_vevent, event, _ignore_calendar, client = self._simpleAccount(
-            userNumber, SIMPLE_EVENT)
-        inviter = Inviter(Clock(), self.sim, client, userNumber)
-        inviter.setParameters(inviteeDistribution=Deterministic(1))
-        inviter._invite()
-        attendees = tuple(event.component.mainComponent().properties('ATTENDEE'))
-        self.assertEquals(len(attendees), 1)
-        for paramname, paramvalue in {
-            'CN': 'User %d' % (userNumber + 1,),
-            'CUTYPE': 'INDIVIDUAL',
-            'PARTSTAT': 'NEEDS-ACTION',
-            'ROLE': 'REQ-PARTICIPANT',
-            'RSVP': 'TRUE'
-        }.items():
-            self.assertTrue(attendees[0].hasParameter(paramname))
-            self.assertEqual(attendees[0].parameterValue(paramname), paramvalue)
-
-
-    def test_doNotAddSelfToEvent(self):
-        &quot;&quot;&quot;
-        If the inviter randomly selects its own user to be added to
-        the attendee list, a different user is added instead.
-        &quot;&quot;&quot;
-        selfNumber = 12
-        _ignore_vevent, event, _ignore_calendar, client = self._simpleAccount(
-            selfNumber, SIMPLE_EVENT)
-
-        otherNumber = 20
-        values = [selfNumber - selfNumber, otherNumber - selfNumber]
-
-        inviter = Inviter(Clock(), self.sim, client, selfNumber)
-        inviter.setParameters(inviteeDistribution=SequentialDistribution(values))
-        inviter._invite()
-        attendees = tuple(event.component.mainComponent().properties('ATTENDEE'))
-        self.assertEquals(len(attendees), 1)
-        for paramname, paramvalue in {
-            'CN': 'User %d' % (otherNumber,),
-            'CUTYPE': 'INDIVIDUAL',
-            'PARTSTAT': 'NEEDS-ACTION',
-            'ROLE': 'REQ-PARTICIPANT',
-            'RSVP': 'TRUE'
-        }.items():
-            self.assertTrue(attendees[0].hasParameter(paramname))
-            self.assertEqual(attendees[0].parameterValue(paramname), paramvalue)
-
-
-    def test_doNotAddExistingToEvent(self):
-        &quot;&quot;&quot;
-        If the inviter randomly selects a user which is already an
-        invitee on the event, a different user is added instead.
-        &quot;&quot;&quot;
-        selfNumber = 1
-        _ignore_vevent, event, _ignore_calendar, client = self._simpleAccount(
-            selfNumber, INVITED_EVENT)
-
-        invitee = tuple(event.component.mainComponent().properties('ATTENDEE'))[0]
-        inviteeNumber = int(invitee.parameterValue('CN').split()[1])
-        anotherNumber = inviteeNumber + 5
-        values = [inviteeNumber - selfNumber, anotherNumber - selfNumber]
-
-        inviter = Inviter(Clock(), self.sim, client, selfNumber)
-        inviter.setParameters(inviteeDistribution=SequentialDistribution(values))
-        inviter._invite()
-        attendees = tuple(event.component.mainComponent().properties('ATTENDEE'))
-        self.assertEquals(len(attendees), 3)
-        for paramname, paramvalue in {
-            'CN': 'User %02d' % (anotherNumber,),
-            'CUTYPE': 'INDIVIDUAL',
-            'PARTSTAT': 'NEEDS-ACTION',
-            'ROLE': 'REQ-PARTICIPANT',
-            'RSVP': 'TRUE'
-        }.items():
-            self.assertTrue(attendees[2].hasParameter(paramname))
-            self.assertEqual(attendees[2].parameterValue(paramname), paramvalue)
-
-
-    def test_everybodyInvitedAlready(self):
-        &quot;&quot;&quot;
-        If the first so-many randomly selected users we come across
-        are already attendees on the event, the invitation attempt is
-        abandoned.
-        &quot;&quot;&quot;
-        selfNumber = 1
-        vevent, _ignore_event, _ignore_calendar, client = self._simpleAccount(
-            selfNumber, INVITED_EVENT)
-        inviter = Inviter(Clock(), self.sim, client, selfNumber)
-        # Always return a user number which has already been invited.
-        inviter.setParameters(inviteeDistribution=Deterministic(2 - selfNumber))
-        inviter._invite()
-        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
-        self.assertEquals(len(attendees), 2)
-
-
-    def test_doNotInviteToSomeoneElsesEvent(self):
-        &quot;&quot;&quot;
-        If there are events on our calendar which are being organized
-        by someone else, the inviter does not attempt to invite new
-        users to them.
-        &quot;&quot;&quot;
-        selfNumber = 2
-        vevent, _ignore_event, _ignore_calendar, client = self._simpleAccount(
-            selfNumber, INVITED_EVENT)
-        inviter = Inviter(None, self.sim, client, selfNumber)
-        # Try to send an invitation, but with only one event on the
-        # calendar, of which we are not the organizer.  It should be
-        # unchanged afterwards.
-        inviter._invite()
-        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
-        self.assertEqual(len(attendees), 2)
-        self.assertEqual(attendees[0].parameterValue('CN'), 'User 01')
-        self.assertEqual(attendees[1].parameterValue('CN'), 'User 02')
-
-
-
-class RealisticInviterTests(TestCase):
-    &quot;&quot;&quot;
-    Tests for loadtest.profiles.RealisticInviter.
-    &quot;&quot;&quot;
-    def setUp(self):
-        self.sim = CalendarClientSimulator(
-            AnyUser(), Populator(None), None, None, None, None, None)
-
-
-    def _simpleAccount(self, userNumber, eventText):
-        client = StubClient(userNumber, self.mktemp())
-        vevent = Component.fromString(eventText)
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'calendar', u'/cal/', None)
-        event = Event(client.serializeLocation(), calendar.url + u'1234.ics', None, vevent)
-        calendar.events = {u'1234.ics': event}
-        client._events.update({event.url: event})
-        client._calendars.update({calendar.url: calendar})
-
-        return vevent, event, calendar, client
-
-
-    def test_enabled(self):
-        userNumber = 13
-        client = StubClient(userNumber, self.mktemp())
-
-        inviter = RealisticInviter(None, self.sim, client, userNumber, **{&quot;enabled&quot;: False})
-        self.assertEqual(inviter.enabled, False)
-
-        inviter = RealisticInviter(None, self.sim, client, userNumber, **{&quot;enabled&quot;: True})
-        self.assertEqual(inviter.enabled, True)
-
-
-    def test_doNotAddInviteToInbox(self):
-        &quot;&quot;&quot;
-        When the only calendar with any events is a schedule inbox, no
-        attempt is made to add attendees to that calendar.
-        &quot;&quot;&quot;
-        calendar = Calendar(
-            caldavxml.schedule_inbox, set(), u'inbox', u'/sched/inbox', None)
-        userNumber = 13
-        client = StubClient(userNumber, self.mktemp())
-        client._calendars.update({calendar.url: calendar})
-
-        inviter = RealisticInviter(None, self.sim, client, userNumber, **{&quot;enabled&quot;: False})
-        inviter._invite()
-
-        self.assertEquals(client._events, {})
-
-
-    def test_doNotAddInviteToNoCalendars(self):
-        &quot;&quot;&quot;
-        When there are no calendars and no events at all, the inviter
-        does nothing.
-        &quot;&quot;&quot;
-        userNumber = 13
-        client = StubClient(userNumber, self.mktemp())
-        inviter = RealisticInviter(None, self.sim, client, userNumber)
-        inviter._invite()
-        self.assertEquals(client._events, {})
-        self.assertEquals(client._calendars, {})
-
-
-    def test_addInvite(self):
-        &quot;&quot;&quot;
-        When there is a normal calendar, inviter adds an invite to it.
-        &quot;&quot;&quot;
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'personal stuff', u'/cals/personal', None)
-        userNumber = 16
-        serializePath = self.mktemp()
-        os.mkdir(serializePath)
-        client = StubClient(userNumber, self.mktemp())
-        client._calendars.update({calendar.url: calendar})
-        inviter = RealisticInviter(Clock(), self.sim, client, userNumber)
-        inviter.setParameters(
-            inviteeDistribution=Deterministic(1),
-            inviteeCountDistribution=Deterministic(1)
-        )
-        inviter._invite()
-        self.assertEquals(len(client._events), 1)
-        attendees = tuple(client._events.values()[0].component.mainComponent().properties('ATTENDEE'))
-        expected = set((&quot;mailto:user%02d@example.com&quot; % (userNumber,), &quot;mailto:user%02d@example.com&quot; % (userNumber + 1,),))
-        for attendee in attendees:
-            expected.remove(attendee.value())
-        self.assertEqual(len(expected), 0)
-
-
-    def test_doNotAddSelfToEvent(self):
-        &quot;&quot;&quot;
-        If the inviter randomly selects its own user to be added to
-        the attendee list, a different user is added instead.
-        &quot;&quot;&quot;
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'personal stuff', u'/cals/personal', None)
-        selfNumber = 12
-        client = StubClient(selfNumber, self.mktemp())
-        client._calendars.update({calendar.url: calendar})
-
-        otherNumber = 20
-        values = [selfNumber - selfNumber, otherNumber - selfNumber]
-
-        inviter = RealisticInviter(Clock(), self.sim, client, selfNumber)
-        inviter.setParameters(
-            inviteeDistribution=SequentialDistribution(values),
-            inviteeCountDistribution=Deterministic(1)
-        )
-        inviter._invite()
-        self.assertEquals(len(client._events), 1)
-        attendees = tuple(client._events.values()[0].component.mainComponent().properties('ATTENDEE'))
-        expected = set((&quot;mailto:user%02d@example.com&quot; % (selfNumber,), &quot;mailto:user%02d@example.com&quot; % (otherNumber,),))
-        for attendee in attendees:
-            expected.remove(attendee.value())
-        self.assertEqual(len(expected), 0)
-
-
-    def test_doNotAddExistingToEvent(self):
-        &quot;&quot;&quot;
-        If the inviter randomly selects a user which is already an
-        invitee on the event, a different user is added instead.
-        &quot;&quot;&quot;
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'personal stuff', u'/cals/personal', None)
-        selfNumber = 1
-        client = StubClient(selfNumber, self.mktemp())
-        client._calendars.update({calendar.url: calendar})
-
-        inviteeNumber = 20
-        anotherNumber = inviteeNumber + 5
-        values = [inviteeNumber - selfNumber, inviteeNumber - selfNumber, anotherNumber - selfNumber]
-
-        inviter = RealisticInviter(Clock(), self.sim, client, selfNumber)
-        inviter.setParameters(
-            inviteeDistribution=SequentialDistribution(values),
-            inviteeCountDistribution=Deterministic(2)
-        )
-        inviter._invite()
-        self.assertEquals(len(client._events), 1)
-        attendees = tuple(client._events.values()[0].component.mainComponent().properties('ATTENDEE'))
-        expected = set((
-            &quot;mailto:user%02d@example.com&quot; % (selfNumber,),
-            &quot;mailto:user%02d@example.com&quot; % (inviteeNumber,),
-            &quot;mailto:user%02d@example.com&quot; % (anotherNumber,),
-        ))
-        for attendee in attendees:
-            expected.remove(attendee.value())
-        self.assertEqual(len(expected), 0)
-
-
-    def test_everybodyInvitedAlready(self):
-        &quot;&quot;&quot;
-        If the first so-many randomly selected users we come across
-        are already attendees on the event, the invitation attempt is
-        abandoned.
-        &quot;&quot;&quot;
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'personal stuff', u'/cals/personal', None)
-        userNumber = 1
-        client = StubClient(userNumber, self.mktemp())
-        client._calendars.update({calendar.url: calendar})
-        inviter = RealisticInviter(Clock(), self.sim, client, userNumber)
-        inviter.setParameters(
-            inviteeDistribution=Deterministic(1),
-            inviteeCountDistribution=Deterministic(2)
-        )
-        inviter._invite()
-        self.assertEquals(len(client._events), 0)
-
-
-
-class AccepterTests(TestCase):
-    &quot;&quot;&quot;
-    Tests for loadtest.profiles.Accepter.
-    &quot;&quot;&quot;
-    def setUp(self):
-        self.sim = CalendarClientSimulator(
-            AnyUser(), Populator(None), None, None, None, None, None)
-
-
-    def test_enabled(self):
-        userNumber = 13
-        client = StubClient(userNumber, self.mktemp())
-
-        accepter = Accepter(None, self.sim, client, userNumber, **{&quot;enabled&quot;: False})
-        self.assertEqual(accepter.enabled, False)
-
-        accepter = Accepter(None, self.sim, client, userNumber, **{&quot;enabled&quot;: True})
-        self.assertEqual(accepter.enabled, True)
-
-
-    def test_ignoreEventOnUnknownCalendar(self):
-        &quot;&quot;&quot;
-        If an event on an unknown calendar changes, it is ignored.
-        &quot;&quot;&quot;
-        userNumber = 13
-        client = StubClient(userNumber, self.mktemp())
-        accepter = Accepter(None, self.sim, client, userNumber)
-        accepter.eventChanged('/some/calendar/1234.ics')
-
-
-    def test_ignoreNonCalendar(self):
-        &quot;&quot;&quot;
-        If an event is on a calendar which is not of type
-        {CALDAV:}calendar, it is ignored.
-        &quot;&quot;&quot;
-        userNumber = 14
-        calendarURL = '/some/calendar/'
-        calendar = Calendar(
-            csxml.dropbox_home, set(), u'notification', calendarURL, None)
-        client = StubClient(userNumber, self.mktemp())
-        client._calendars[calendarURL] = calendar
-        accepter = Accepter(None, self.sim, client, userNumber)
-        accepter.eventChanged(calendarURL + '1234.ics')
-
-
-    def test_ignoreAccepted(self):
-        &quot;&quot;&quot;
-        If the client is an attendee on an event but the PARTSTAT is
-        not NEEDS-ACTION, the event is ignored.
-        &quot;&quot;&quot;
-        vevent = Component.fromString(ACCEPTED_EVENT)
-        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
-        userNumber = int(attendees[1].parameterValue('CN').split(None, 1)[1])
-        calendarURL = '/some/calendar/'
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'calendar', calendarURL, None)
-        client = StubClient(userNumber, self.mktemp())
-        client._calendars[calendarURL] = calendar
-        event = Event(client.serializeLocation(), calendarURL + u'1234.ics', None, vevent)
-        client._events[event.url] = event
-        accepter = Accepter(None, self.sim, client, userNumber)
-        accepter.eventChanged(event.url)
-
-
-    def test_ignoreAlreadyAccepting(self):
-        &quot;&quot;&quot;
-        If the client sees an event change a second time before
-        responding to an invitation found on it during the first
-        change notification, the second change notification does not
-        generate another accept attempt.
-        &quot;&quot;&quot;
-        clock = Clock()
-        randomDelay = 7
-        vevent = Component.fromString(INVITED_EVENT)
-        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
-        userNumber = int(attendees[1].parameterValue('CN').split(None, 1)[1])
-        calendarURL = '/some/calendar/'
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'calendar', calendarURL, None)
-        client = StubClient(userNumber, self.mktemp())
-        client._calendars[calendarURL] = calendar
-        event = Event(client.serializeLocation(), calendarURL + u'1234.ics', None, vevent)
-        client._events[event.url] = event
-        accepter = Accepter(clock, self.sim, client, userNumber)
-        accepter.random = Deterministic()
-
-        def _gauss(mu, sigma):
-            return randomDelay
-        accepter.random.gauss = _gauss
-        accepter.eventChanged(event.url)
-        accepter.eventChanged(event.url)
-        clock.advance(randomDelay)
-
-
-    def test_inboxReply(self):
-        &quot;&quot;&quot;
-        When an inbox item that contains a reply is seen by the client, it
-        deletes it immediately.
-        &quot;&quot;&quot;
-        userNumber = 1
-        clock = Clock()
-        inboxURL = '/some/inbox/'
-        vevent = Component.fromString(INBOX_REPLY)
-        inbox = Calendar(
-            caldavxml.schedule_inbox, set(), u'the inbox', inboxURL, None)
-        client = StubClient(userNumber, self.mktemp())
-        client._calendars[inboxURL] = inbox
-
-        inboxEvent = Event(client.serializeLocation(), inboxURL + u'4321.ics', None, vevent)
-        client._setEvent(inboxEvent.url, inboxEvent)
-        accepter = Accepter(clock, self.sim, client, userNumber)
-        accepter.eventChanged(inboxEvent.url)
-        clock.advance(3)
-        self.assertNotIn(inboxEvent.url, client._events)
-        self.assertNotIn('4321.ics', inbox.events)
-
-
-    def test_inboxReplyFailedDelete(self):
-        &quot;&quot;&quot;
-        When an inbox item that contains a reply is seen by the client, it
-        deletes it immediately.  If the delete fails, the appropriate response
-        code is returned.
-        &quot;&quot;&quot;
-        userNumber = 1
-        clock = Clock()
-        inboxURL = '/some/inbox/'
-        vevent = Component.fromString(INBOX_REPLY)
-        inbox = Calendar(
-            caldavxml.schedule_inbox, set(), u'the inbox', inboxURL, None)
-        client = StubClient(userNumber, self.mktemp())
-        client._calendars[inboxURL] = inbox
-
-        inboxEvent = Event(client.serializeLocation(), inboxURL + u'4321.ics', None, vevent)
-        client._setEvent(inboxEvent.url, inboxEvent)
-        client._failDeleteWithObject(inboxEvent.url, IncorrectResponseCode(
-            NO_CONTENT,
-            Response(
-                ('HTTP', 1, 1), PRECONDITION_FAILED,
-                'Precondition Failed', None, None))
-        )
-        accepter = Accepter(clock, self.sim, client, userNumber)
-        accepter.eventChanged(inboxEvent.url)
-        clock.advance(3)
-        self.assertNotIn(inboxEvent.url, client._events)
-        self.assertNotIn('4321.ics', inbox.events)
-
-
-    def test_acceptInvitation(self):
-        &quot;&quot;&quot;
-        If the client is an attendee on an event and the PARTSTAT is
-        NEEDS-ACTION, a response is generated which accepts the
-        invitation and the corresponding event in the
-        I{schedule-inbox} is deleted.
-        &quot;&quot;&quot;
-        clock = Clock()
-        randomDelay = 7
-        vevent = Component.fromString(INVITED_EVENT)
-        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
-        userNumber = int(attendees[1].parameterValue('CN').split(None, 1)[1])
-        client = StubClient(userNumber, self.mktemp())
-
-        calendarURL = '/some/calendar/'
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'calendar', calendarURL, None)
-        client._calendars[calendarURL] = calendar
-
-        inboxURL = '/some/inbox/'
-        inbox = Calendar(
-            caldavxml.schedule_inbox, set(), u'the inbox', inboxURL, None)
-        client._calendars[inboxURL] = inbox
-
-        event = Event(client.serializeLocation(), calendarURL + u'1234.ics', None, vevent)
-        client._setEvent(event.url, event)
-
-        inboxEvent = Event(client.serializeLocation(), inboxURL + u'4321.ics', None, vevent)
-        client._setEvent(inboxEvent.url, inboxEvent)
-
-        accepter = Accepter(clock, self.sim, client, userNumber)
-        accepter.setParameters(acceptDelayDistribution=Deterministic(randomDelay))
-        accepter.eventChanged(event.url)
-        clock.advance(randomDelay)
-
-        vevent = client._events[event.url].component
-        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
-        self.assertEquals(len(attendees), 2)
-        self.assertEquals(
-            attendees[1].parameterValue('CN'), 'User %02d' % (userNumber,))
-        self.assertEquals(
-            attendees[1].parameterValue('PARTSTAT'), 'ACCEPTED')
-        self.assertFalse(attendees[1].hasParameter('RSVP'))
-
-        self.assertNotIn(inboxEvent.url, client._events)
-        self.assertNotIn('4321.ics', inbox.events)
-
-
-    def test_reacceptInvitation(self):
-        &quot;&quot;&quot;
-        If a client accepts an invitation on an event and then is
-        later re-invited to the same event, the invitation is again
-        accepted.
-        &quot;&quot;&quot;
-        clock = Clock()
-        randomDelay = 7
-        vevent = Component.fromString(INVITED_EVENT)
-        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
-        userNumber = int(attendees[1].parameterValue('CN').split(None, 1)[1])
-        calendarURL = '/some/calendar/'
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'calendar', calendarURL, None)
-        client = StubClient(userNumber, self.mktemp())
-        client._calendars[calendarURL] = calendar
-        event = Event(client.serializeLocation(), calendarURL + u'1234.ics', None, vevent)
-        client._events[event.url] = event
-        accepter = Accepter(clock, self.sim, client, userNumber)
-        accepter.setParameters(acceptDelayDistribution=Deterministic(randomDelay))
-        accepter.eventChanged(event.url)
-        clock.advance(randomDelay)
-
-        # Now re-set the event so it has to be accepted again
-        event.component = Component.fromString(INVITED_EVENT)
-
-        # And now re-deliver it
-        accepter.eventChanged(event.url)
-        clock.advance(randomDelay)
-
-        # And ensure that it was accepted again
-        vevent = client._events[event.url].component
-        attendees = tuple(vevent.mainComponent().properties('ATTENDEE'))
-        self.assertEquals(len(attendees), 2)
-        self.assertEquals(
-            attendees[1].parameterValue('CN'), 'User %02d' % (userNumber,))
-        self.assertEquals(
-            attendees[1].parameterValue('PARTSTAT'), 'ACCEPTED')
-        self.assertFalse(attendees[1].hasParameter('RSVP'))
-
-
-    def test_changeEventAttendeePreconditionFailed(self):
-        &quot;&quot;&quot;
-        If the attempt to accept an invitation fails because of an
-        unmet precondition (412), the event is re-retrieved and the
-        PUT is re-issued with the new data.
-        &quot;&quot;&quot;
-        clock = Clock()
-        userNumber = 2
-        client = StubClient(userNumber, self.mktemp())
-        randomDelay = 3
-
-        calendarURL = '/some/calendar/'
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'calendar', calendarURL, None)
-        client._calendars[calendarURL] = calendar
-
-        vevent = Component.fromString(INVITED_EVENT)
-        event = Event(client.serializeLocation(), calendarURL + u'1234.ics', None, vevent)
-        client._setEvent(event.url, event)
-
-        accepter = Accepter(clock, self.sim, client, userNumber)
-        accepter.setParameters(acceptDelayDistribution=Deterministic(randomDelay))
-
-        client.rescheduled.add(event.url)
-
-        accepter.eventChanged(event.url)
-        clock.advance(randomDelay)
-
-
-
-class EventerTests(TestCase):
-    &quot;&quot;&quot;
-    Tests for loadtest.profiles.Eventer, a profile which adds new
-    events on calendars.
-    &quot;&quot;&quot;
-    def setUp(self):
-        self.sim = CalendarClientSimulator(
-            AnyUser(), Populator(None), None, None, None, None, None)
-
-
-    def test_enabled(self):
-        userNumber = 13
-        client = StubClient(userNumber, self.mktemp())
-
-        eventer = Eventer(None, self.sim, client, None, **{&quot;enabled&quot;: False})
-        self.assertEqual(eventer.enabled, False)
-
-        eventer = Eventer(None, self.sim, client, None, **{&quot;enabled&quot;: True})
-        self.assertEqual(eventer.enabled, True)
-
-
-    def test_doNotAddEventOnInbox(self):
-        &quot;&quot;&quot;
-        When the only calendar is a schedule inbox, no attempt is made
-        to add events on it.
-        &quot;&quot;&quot;
-        calendar = Calendar(
-            caldavxml.schedule_inbox, set(), u'inbox', u'/sched/inbox', None)
-        client = StubClient(21, self.mktemp())
-        client._calendars.update({calendar.url: calendar})
-
-        eventer = Eventer(None, self.sim, client, None)
-        eventer._addEvent()
-
-        self.assertEquals(client._events, {})
-
-
-    def test_addEvent(self):
-        &quot;&quot;&quot;
-        When there is a normal calendar to add events to,
-        L{Eventer._addEvent} adds an event to it.
-        &quot;&quot;&quot;
-        calendar = Calendar(
-            caldavxml.calendar, set(('VEVENT',)), u'personal stuff', u'/cals/personal', None)
-        client = StubClient(31, self.mktemp())
-        client._calendars.update({calendar.url: calendar})
-
-        eventer = Eventer(Clock(), self.sim, client, None)
-        eventer._addEvent()
-
-        self.assertEquals(len(client._events), 1)
-
-        # XXX Vary the event period/interval and the uid
-
-
-
-class OperationLoggerTests(TestCase):
-    &quot;&quot;&quot;
-    Tests for L{OperationLogger}.
-    &quot;&quot;&quot;
-    def test_noFailures(self):
-        &quot;&quot;&quot;
-        If the median lag is below 1 second and the failure rate is below 1%,
-        L{OperationLogger.failures} returns an empty list.
-        &quot;&quot;&quot;
-        logger = OperationLogger(outfile=StringIO())
-        logger.observe(dict(
-            type='operation', phase='start', user='user01',
-            label='testing', lag=0.5)
-        )
-        logger.observe(dict(
-            type='operation', phase='end', user='user01',
-            duration=0.35, label='testing', success=True)
-        )
-        self.assertEqual([], logger.failures())
-
-
-    def test_lagLimitExceeded(self):
-        &quot;&quot;&quot;
-        If the median scheduling lag for any operation in the simulation
-        exceeds 1 second, L{OperationLogger.failures} returns a list containing
-        a string describing that issue.
-        &quot;&quot;&quot;
-        logger = OperationLogger(outfile=StringIO())
-        for lag in [100.0, 1100.0, 1200.0]:
-            logger.observe(dict(
-                type='operation', phase='start', user='user01',
-                label='testing', lag=lag)
-            )
-        self.assertEqual(
-            [&quot;Median TESTING scheduling lag greater than 1000.0ms&quot;],
-            logger.failures())
-
-
-    def test_failureLimitExceeded(self):
-        &quot;&quot;&quot;
-        If the failure rate for any operation exceeds 1%,
-        L{OperationLogger.failures} returns a list containing a string
-        describing that issue.
-        &quot;&quot;&quot;
-        logger = OperationLogger(outfile=StringIO())
-        for _ignore in range(98):
-            logger.observe(dict(
-                type='operation', phase='end', user='user01',
-                duration=0.25, label='testing', success=True)
-            )
-        logger.observe(dict(
-            type='operation', phase='end', user='user01',
-            duration=0.25, label='testing', success=False)
-        )
-        self.assertEqual(
-            [&quot;Greater than 1% TESTING failed&quot;],
-            logger.failures())
</del></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_simpy"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_sim.py (15047 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_sim.py        2015-08-17 20:38:08 UTC (rev 15047)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_sim.py        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -1,592 +0,0 @@
</span><del>-##
-# Copyright (c) 2011-2015 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-##
-
-from plistlib import writePlistToString
-from cStringIO import StringIO
-
-from twisted.python.log import msg
-from twisted.python.usage import UsageError
-from twisted.python.filepath import FilePath
-from twisted.internet.defer import Deferred, succeed
-from twisted.trial.unittest import TestCase
-
-from contrib.performance.stats import NormalDistribution
-from contrib.performance.loadtest.ical import OS_X_10_6
-from contrib.performance.loadtest.profiles import Eventer, Inviter, Accepter
-from contrib.performance.loadtest.population import (
-    SmoothRampUp, ClientType, PopulationParameters, Populator, CalendarClientSimulator,
-    ProfileType, SimpleStatistics
-)
-from contrib.performance.loadtest.sim import (
-    Arrival, SimOptions, LoadSimulator, LagTrackingReactor,
-    _DirectoryRecord
-)
-
-
-VALID_CONFIG = {
-    'server': 'tcp:127.0.0.1:8008',
-    'webadmin': {
-        'enabled': True,
-        'HTTPPort': 8080,
-    },
-    'arrival': {
-        'factory': 'contrib.performance.loadtest.population.SmoothRampUp',
-        'params': {
-            'groups': 10,
-            'groupSize': 1,
-            'interval': 3,
-        },
-    },
-}
-
-VALID_CONFIG_PLIST = writePlistToString(VALID_CONFIG)
-
-
-
-class SimOptionsTests(TestCase):
-    def test_defaultConfig(self):
-        &quot;&quot;&quot;
-        If the I{config} option is not specified, the default config.plist in
-        the source tree is used.
-        &quot;&quot;&quot;
-        options = SimOptions()
-        self.assertEqual(options['config'], FilePath(__file__).sibling('config.plist'))
-
-
-    def test_configFileNotFound(self):
-        &quot;&quot;&quot;
-        If the filename given to the I{config} option is not found,
-        L{SimOptions.parseOptions} raises a L{UsageError} indicating
-        this.
-        &quot;&quot;&quot;
-        name = FilePath(self.mktemp())
-        options = SimOptions()
-        exc = self.assertRaises(
-            UsageError, options.parseOptions, ['--config', name.path])
-        self.assertEquals(
-            str(exc), &quot;--config %s: No such file or directory&quot; % (name.path,))
-
-
-    def test_configFileNotParseable(self):
-        &quot;&quot;&quot;
-        If the contents of the file given to the I{config} option
-        cannot be parsed by L{ConfigParser},
-        L{SimOptions.parseOptions} raises a L{UsageError} indicating
-        this.
-        &quot;&quot;&quot;
-        config = FilePath(self.mktemp())
-        config.setContent(&quot;some random junk&quot;)
-        options = SimOptions()
-        exc = self.assertRaises(
-            UsageError, options.parseOptions, ['--config', config.path])
-        self.assertEquals(
-            str(exc),
-            &quot;--config %s: syntax error: line 1, column 0&quot; % (config.path,))
-
-
-
-class CalendarClientSimulatorTests(TestCase):
-    &quot;&quot;&quot;
-    Tests for L{CalendarClientSimulator} which adds running clients to
-    a simulation.
-    &quot;&quot;&quot;
-    realmName = 'stub'
-
-    def _user(self, name):
-        password = 'password-' + name
-        email = name + &quot;@example.com&quot;
-        record = _DirectoryRecord(name, password, name, email, name)
-        return record
-
-
-    def test_createUser(self):
-        &quot;&quot;&quot;
-        Subsequent calls to L{CalendarClientSimulator._createUser}
-        with different user numbers return user details from different
-        directory records.
-        &quot;&quot;&quot;
-        calsim = CalendarClientSimulator(
-            [self._user('alice'), self._user('bob'), self._user('carol')],
-            Populator(None), None, None, 'http://example.org:1234/', None, None)
-        users = sorted([
-            calsim._createUser(0)[0],
-            calsim._createUser(1)[0],
-            calsim._createUser(2)[0],
-        ])
-        self.assertEqual(['alice', 'bob', 'carol'], users)
-
-
-    def test_createUserAuthInfo(self):
-        &quot;&quot;&quot;
-        The auth handler returned by L{CalendarClientSimulator._createUser}
-        includes the password taken from user's directory record.
-        &quot;&quot;&quot;
-        calsim = CalendarClientSimulator(
-            [self._user('alice')],
-            Populator(None), None, None, 'http://example.org:1234/', None, None)
-        user, auth = calsim._createUser(0)
-        self.assertEqual(
-            auth['basic'].passwd.find_user_password('Test Realm', 'http://example.org:1234/')[1],
-            'password-' + user)
-        self.assertEqual(
-            auth['digest'].passwd.find_user_password('Test Realm', 'http://example.org:1234/')[1],
-            'password-' + user)
-
-
-    def test_stop(self):
-        &quot;&quot;&quot;
-        After L{CalendarClientSimulator.stop} is called, failed clients and
-        profiles are not logged.
-        &quot;&quot;&quot;
-        class BrokenClient(object):
-            def __init__(self, reactor, serverAddress, principalPathTemplate, serializationPath, userInfo, auth, runResult):
-                self._runResult = runResult
-
-            def run(self):
-                return self._runResult
-
-            def stop(self):
-                return succeed(None)
-
-        class BrokenProfile(object):
-            def __init__(self, reactor, simulator, client, userNumber, runResult):
-                self._runResult = runResult
-                self.enabled = True
-
-            def initialize(self):
-                return succeed(None)
-
-            def run(self):
-                return self._runResult
-
-        clientRunResult = Deferred()
-        profileRunResult = Deferred()
-
-        params = PopulationParameters()
-        params.addClient(1, ClientType(
-            BrokenClient, {'runResult': clientRunResult},
-            [ProfileType(BrokenProfile, {'runResult': profileRunResult})])
-        )
-        sim = CalendarClientSimulator(
-            [self._user('alice')], Populator(None), params, None, 'http://example.com:1234/', None, None)
-        sim.add(1, 1)
-        sim.stop()
-        clientRunResult.errback(RuntimeError(&quot;Some fictional client problem&quot;))
-        profileRunResult.errback(RuntimeError(&quot;Some fictional profile problem&quot;))
-
-        self.assertEqual([], self.flushLoggedErrors())
-
-
-
-class Reactor(object):
-    message = &quot;some event to be observed&quot;
-
-    def __init__(self):
-        self._triggers = []
-        self._whenRunning = []
-
-
-    def run(self):
-        for thunk in self._whenRunning:
-            thunk()
-        msg(thingo=self.message)
-        for _ignore_phase, event, thunk in self._triggers:
-            if event == 'shutdown':
-                thunk()
-
-
-    def callWhenRunning(self, thunk):
-        self._whenRunning.append(thunk)
-
-
-    def addSystemEventTrigger(self, phase, event, thunk):
-        self._triggers.append((phase, event, thunk))
-
-
-
-class Observer(object):
-    def __init__(self):
-        self.reported = False
-        self.events = []
-
-
-    def observe(self, event):
-        self.events.append(event)
-
-
-    def report(self, output):
-        self.reported = True
-
-
-    def failures(self):
-        return []
-
-
-
-class NullArrival(object):
-    def run(self, sim):
-        pass
-
-
-
-class StubSimulator(LoadSimulator):
-    def run(self):
-        return 3
-
-
-
-class LoadSimulatorTests(TestCase):
-    def test_main(self):
-        &quot;&quot;&quot;
-        L{LoadSimulator.main} raises L{SystemExit} with the result of
-        L{LoadSimulator.run}.
-        &quot;&quot;&quot;
-        config = FilePath(self.mktemp())
-        config.setContent(VALID_CONFIG_PLIST)
-
-        exc = self.assertRaises(
-            SystemExit, StubSimulator.main, ['--config', config.path])
-        self.assertEquals(
-            exc.args, (StubSimulator(None, None, None, None, None, None, None).run(),))
-
-
-    def test_createSimulator(self):
-        &quot;&quot;&quot;
-        L{LoadSimulator.createSimulator} creates a L{CalendarClientSimulator}
-        with its own reactor and host and port information from the
-        configuration file.
-        &quot;&quot;&quot;
-        server = 'http://127.0.0.7:1243/'
-        reactor = object()
-        sim = LoadSimulator(server, None, None, None, None, None, None, reactor=reactor)
-        calsim = sim.createSimulator()
-        self.assertIsInstance(calsim, CalendarClientSimulator)
-        self.assertIsInstance(calsim.reactor, LagTrackingReactor)
-        self.assertIdentical(calsim.reactor._reactor, reactor)
-        self.assertEquals(calsim.server, server)
-
-
-    def test_loadAccountsFromFile(self):
-        &quot;&quot;&quot;
-        L{LoadSimulator.fromCommandLine} takes an account loader from the
-        config file and uses it to create user records for use in the
-        simulation.
-        &quot;&quot;&quot;
-        accounts = FilePath(self.mktemp())
-        accounts.setContent(&quot;foo,bar,baz,quux,goo\nfoo2,bar2,baz2,quux2,goo2\n&quot;)
-        config = VALID_CONFIG.copy()
-        config[&quot;accounts&quot;] = {
-            &quot;loader&quot;: &quot;contrib.performance.loadtest.sim.recordsFromCSVFile&quot;,
-            &quot;params&quot;: {
-                &quot;path&quot;: accounts.path
-            },
-        }
-        configpath = FilePath(self.mktemp())
-        configpath.setContent(writePlistToString(config))
-        io = StringIO()
-        sim = LoadSimulator.fromCommandLine(['--config', configpath.path], io)
-        self.assertEquals(io.getvalue(), &quot;Loaded 2 accounts.\n&quot;)
-        self.assertEqual(2, len(sim.records))
-        self.assertEqual(sim.records[0].uid, 'foo')
-        self.assertEqual(sim.records[0].password, 'bar')
-        self.assertEqual(sim.records[0].commonName, 'baz')
-        self.assertEqual(sim.records[0].email, 'quux')
-        self.assertEqual(sim.records[1].uid, 'foo2')
-        self.assertEqual(sim.records[1].password, 'bar2')
-        self.assertEqual(sim.records[1].commonName, 'baz2')
-        self.assertEqual(sim.records[1].email, 'quux2')
-
-
-    def test_loadDefaultAccountsFromFile(self):
-        &quot;&quot;&quot;
-        L{LoadSimulator.fromCommandLine} takes an account loader (with
-        empty path)from the config file and uses it to create user
-        records for use in the simulation.
-        &quot;&quot;&quot;
-        config = VALID_CONFIG.copy()
-        config[&quot;accounts&quot;] = {
-            &quot;loader&quot;: &quot;contrib.performance.loadtest.sim.recordsFromCSVFile&quot;,
-            &quot;params&quot;: {
-                &quot;path&quot;: &quot;&quot;
-            },
-        }
-        configpath = FilePath(self.mktemp())
-        configpath.setContent(writePlistToString(config))
-        sim = LoadSimulator.fromCommandLine(['--config', configpath.path],
-                                            StringIO())
-        self.assertEqual(99, len(sim.records))
-        self.assertEqual(sim.records[0].uid, 'user01')
-        self.assertEqual(sim.records[0].password, 'user01')
-        self.assertEqual(sim.records[0].commonName, 'User 01')
-        self.assertEqual(sim.records[0].email, 'user01@example.com')
-        self.assertEqual(sim.records[98].uid, 'user99')
-        self.assertEqual(sim.records[98].password, 'user99')
-        self.assertEqual(sim.records[98].commonName, 'User 99')
-        self.assertEqual(sim.records[98].email, 'user99@example.com')
-
-
-    def test_generateRecordsDefaultPatterns(self):
-        &quot;&quot;&quot;
-        L{LoadSimulator.fromCommandLine} takes an account loader from the
-        config file and uses it to generate user records for use in the
-        simulation.
-        &quot;&quot;&quot;
-        config = VALID_CONFIG.copy()
-        config[&quot;accounts&quot;] = {
-            &quot;loader&quot;: &quot;contrib.performance.loadtest.sim.generateRecords&quot;,
-            &quot;params&quot;: {
-                &quot;count&quot;: 2
-            },
-        }
-        configpath = FilePath(self.mktemp())
-        configpath.setContent(writePlistToString(config))
-        sim = LoadSimulator.fromCommandLine(['--config', configpath.path],
-                                            StringIO())
-        self.assertEqual(2, len(sim.records))
-        self.assertEqual(sim.records[0].uid, 'user1')
-        self.assertEqual(sim.records[0].password, 'user1')
-        self.assertEqual(sim.records[0].commonName, 'User 1')
-        self.assertEqual(sim.records[0].email, 'user1@example.com')
-        self.assertEqual(sim.records[1].uid, 'user2')
-        self.assertEqual(sim.records[1].password, 'user2')
-        self.assertEqual(sim.records[1].commonName, 'User 2')
-        self.assertEqual(sim.records[1].email, 'user2@example.com')
-
-
-    def test_generateRecordsNonDefaultPatterns(self):
-        &quot;&quot;&quot;
-        L{LoadSimulator.fromCommandLine} takes an account loader from the
-        config file and uses it to generate user records for use in the
-        simulation.
-        &quot;&quot;&quot;
-        config = VALID_CONFIG.copy()
-        config[&quot;accounts&quot;] = {
-            &quot;loader&quot;: &quot;contrib.performance.loadtest.sim.generateRecords&quot;,
-            &quot;params&quot;: {
-                &quot;count&quot;: 3,
-                &quot;uidPattern&quot;: &quot;USER%03d&quot;,
-                &quot;passwordPattern&quot;: &quot;PASSWORD%03d&quot;,
-                &quot;namePattern&quot;: &quot;Test User %03d&quot;,
-                &quot;emailPattern&quot;: &quot;USER%03d@example2.com&quot;,
-            },
-        }
-        configpath = FilePath(self.mktemp())
-        configpath.setContent(writePlistToString(config))
-        sim = LoadSimulator.fromCommandLine(['--config', configpath.path],
-                                            StringIO())
-        self.assertEqual(3, len(sim.records))
-        self.assertEqual(sim.records[0].uid, 'USER001')
-        self.assertEqual(sim.records[0].password, 'PASSWORD001')
-        self.assertEqual(sim.records[0].commonName, 'Test User 001')
-        self.assertEqual(sim.records[0].email, 'USER001@example2.com')
-        self.assertEqual(sim.records[2].uid, 'USER003')
-        self.assertEqual(sim.records[2].password, 'PASSWORD003')
-        self.assertEqual(sim.records[2].commonName, 'Test User 003')
-        self.assertEqual(sim.records[2].email, 'USER003@example2.com')
-
-
-    def test_specifyRuntime(self):
-        &quot;&quot;&quot;
-        L{LoadSimulator.fromCommandLine} recognizes the I{--runtime} option to
-        specify a limit on how long the simulation will run.
-        &quot;&quot;&quot;
-        config = FilePath(self.mktemp())
-        config.setContent(VALID_CONFIG_PLIST)
-        sim = LoadSimulator.fromCommandLine(['--config', config.path, '--runtime', '123'])
-        self.assertEqual(123, sim.runtime)
-
-
-    def test_loadServerConfig(self):
-        &quot;&quot;&quot;
-        The Calendar Server host and port are loaded from the [server]
-        section of the configuration file specified.
-        &quot;&quot;&quot;
-        config = FilePath(self.mktemp())
-        config.setContent(
-            writePlistToString({&quot;server&quot;: &quot;https://127.0.0.3:8432/&quot;})
-        )
-        sim = LoadSimulator.fromCommandLine(['--config', config.path])
-        self.assertEquals(sim.server, &quot;https://127.0.0.3:8432/&quot;)
-
-
-    def test_loadArrivalConfig(self):
-        &quot;&quot;&quot;
-        The arrival policy type and arguments are loaded from the
-        [arrival] section of the configuration file specified.
-        &quot;&quot;&quot;
-        config = FilePath(self.mktemp())
-        config.setContent(
-            writePlistToString({
-                &quot;arrival&quot;: {
-                    &quot;factory&quot;: &quot;contrib.performance.loadtest.population.SmoothRampUp&quot;,
-                    &quot;params&quot;: {
-                        &quot;groups&quot;: 10,
-                        &quot;groupSize&quot;: 1,
-                        &quot;interval&quot;: 3,
-                    },
-                },
-            })
-        )
-        sim = LoadSimulator.fromCommandLine(['--config', config.path])
-        self.assertEquals(
-            sim.arrival,
-            Arrival(SmoothRampUp, dict(groups=10, groupSize=1, interval=3)))
-
-
-    def test_createArrivalPolicy(self):
-        &quot;&quot;&quot;
-        L{LoadSimulator.createArrivalPolicy} creates an arrival
-        policy based on the L{Arrival} passed to its initializer.
-        &quot;&quot;&quot;
-        class FakeArrival(object):
-            def __init__(self, reactor, x, y):
-                self.reactor = reactor
-                self.x = x
-                self.y = y
-
-        reactor = object()
-        sim = LoadSimulator(
-            None, None, None, None, None, Arrival(FakeArrival, {'x': 3, 'y': 2}), None, reactor=reactor)
-        arrival = sim.createArrivalPolicy()
-        self.assertIsInstance(arrival, FakeArrival)
-        self.assertIdentical(arrival.reactor, sim.reactor)
-        self.assertEquals(arrival.x, 3)
-        self.assertEquals(arrival.y, 2)
-
-
-    def test_loadPopulationParameters(self):
-        &quot;&quot;&quot;
-        Client weights and profiles are loaded from the [clients]
-        section of the configuration file specified.
-        &quot;&quot;&quot;
-        config = FilePath(self.mktemp())
-        config.setContent(
-            writePlistToString(
-                {
-                    &quot;clients&quot;: [
-                        {
-                            &quot;software&quot;: &quot;contrib.performance.loadtest.ical.OS_X_10_6&quot;,
-                            &quot;params&quot;: {
-                                &quot;foo&quot;: &quot;bar&quot;
-                            },
-                            &quot;profiles&quot;: [
-                                {
-                                    &quot;params&quot;: {
-                                        &quot;interval&quot;: 25,
-                                        &quot;eventStartDistribution&quot;: {
-                                            &quot;type&quot;: &quot;contrib.performance.stats.NormalDistribution&quot;,
-                                            &quot;params&quot;: {
-                                                &quot;mu&quot;: 123,
-                                                &quot;sigma&quot;: 456,
-                                            }
-                                        }
-                                    },
-                                    &quot;class&quot;: &quot;contrib.performance.loadtest.profiles.Eventer&quot;
-                                }
-                            ],
-                            &quot;weight&quot;: 3,
-                        }
-                    ]
-                }
-            )
-        )
-
-        sim = LoadSimulator.fromCommandLine(
-            ['--config', config.path, '--clients', config.path]
-        )
-        expectedParameters = PopulationParameters()
-        expectedParameters.addClient(
-            3,
-            ClientType(
-                OS_X_10_6,
-                {&quot;foo&quot;: &quot;bar&quot;},
-                [
-                    ProfileType(
-                        Eventer, {
-                            &quot;interval&quot;: 25,
-                            &quot;eventStartDistribution&quot;: NormalDistribution(123, 456)
-                        }
-                    )
-                ]
-            )
-        )
-        self.assertEquals(sim.parameters, expectedParameters)
-
-
-    def test_requireClient(self):
-        &quot;&quot;&quot;
-        At least one client is required, so if a configuration with an
-        empty clients array is specified, a single default client type
-        is used.
-        &quot;&quot;&quot;
-        config = FilePath(self.mktemp())
-        config.setContent(writePlistToString({&quot;clients&quot;: []}))
-        sim = LoadSimulator.fromCommandLine(
-            ['--config', config.path, '--clients', config.path]
-        )
-        expectedParameters = PopulationParameters()
-        expectedParameters.addClient(
-            1, ClientType(OS_X_10_6, {}, [Eventer, Inviter, Accepter]))
-        self.assertEquals(sim.parameters, expectedParameters)
-
-
-    def test_loadLogObservers(self):
-        &quot;&quot;&quot;
-        Log observers specified in the [observers] section of the
-        configuration file are added to the logging system.
-        &quot;&quot;&quot;
-        config = FilePath(self.mktemp())
-        config.setContent(
-            writePlistToString(
-                {
-                    &quot;observers&quot;: [
-                        {
-                            &quot;type&quot;: &quot;contrib.performance.loadtest.population.SimpleStatistics&quot;,
-                            &quot;params&quot;: {},
-                        },
-                    ]
-                }
-            )
-        )
-        sim = LoadSimulator.fromCommandLine(['--config', config.path])
-        self.assertEquals(len(sim.observers), 1)
-        self.assertIsInstance(sim.observers[0], SimpleStatistics)
-
-
-    def test_observeRunReport(self):
-        &quot;&quot;&quot;
-        Each log observer is added to the log publisher before the
-        simulation run is started and has its C{report} method called
-        after the simulation run completes.
-        &quot;&quot;&quot;
-        observers = [Observer()]
-        sim = LoadSimulator(
-            &quot;http://example.com:123/&quot;,
-            &quot;/principals/users/%s/&quot;,
-            None,
-            None,
-            None,
-            Arrival(lambda reactor: NullArrival(), {}),
-            None, observers, reactor=Reactor())
-        io = StringIO()
-        sim.run(io)
-        self.assertEquals(io.getvalue(), &quot;\n*** PASS\n&quot;)
-        self.assertTrue(observers[0].reported)
-        self.assertEquals(
-            [e for e in observers[0].events if &quot;thingo&quot; in e][0][&quot;thingo&quot;],
-            Reactor.message
-        )
</del></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_trafficloggerpy"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_trafficlogger.py (15047 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_trafficlogger.py        2015-08-17 20:38:08 UTC (rev 15047)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_trafficlogger.py        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -1,210 +0,0 @@
</span><del>-##
-# Copyright (c) 2011-2015 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-##
-
-from zope.interface import Interface, implements
-
-from twisted.internet.protocol import ClientFactory, Protocol
-from twisted.trial.unittest import TestCase
-from twisted.test.proto_helpers import StringTransport, MemoryReactor
-from twisted.protocols.wire import Discard
-
-from contrib.performance.loadtest.trafficlogger import _TrafficLoggingFactory, loggedReactor
-
-
-class IProbe(Interface):
-    &quot;&quot;&quot;
-    An interface which can be used to verify some interface-related behavior of
-    L{loggedReactor}.
-    &quot;&quot;&quot;
-    def probe(): #@NoSelf
-        pass
-
-
-
-class Probe(object):
-    implements(IProbe)
-
-    _probed = False
-
-    def __init__(self, result=None):
-        self._result = result
-
-
-    def probe(self):
-        self._probed = True
-        return self._result
-
-
-
-class TrafficLoggingReactorTests(TestCase):
-    &quot;&quot;&quot;
-    Tests for L{loggedReactor}.
-    &quot;&quot;&quot;
-    def test_nothing(self):
-        &quot;&quot;&quot;
-        L{loggedReactor} returns the object passed to it, if the object passed
-        to it doesn't provide any interfaces.  This is mostly for testing
-        convenience rather than a particularly useful feature.
-        &quot;&quot;&quot;
-        probe = object()
-        self.assertIdentical(probe, loggedReactor(probe))
-
-
-    def test_interfaces(self):
-        &quot;&quot;&quot;
-        The object returned by L{loggedReactor} provides all of the interfaces
-        provided by the object passed to it.
-        &quot;&quot;&quot;
-        probe = Probe()
-        reactor = loggedReactor(probe)
-        self.assertTrue(IProbe.providedBy(reactor))
-
-
-    def test_passthrough(self):
-        &quot;&quot;&quot;
-        Methods on interfaces on the object passed to L{loggedReactor} can be
-        called by calling them on the object returned by L{loggedReactor}.
-        &quot;&quot;&quot;
-        expected = object()
-        probe = Probe(expected)
-        reactor = loggedReactor(probe)
-        result = reactor.probe()
-        self.assertTrue(probe._probed)
-        self.assertIdentical(expected, result)
-
-
-    def test_connectTCP(self):
-        &quot;&quot;&quot;
-        Called on the object returned by L{loggedReactor}, C{connectTCP} calls
-        the wrapped reactor's C{connectTCP} method with the original factory
-        wrapped in a L{_TrafficLoggingFactory}.
-        &quot;&quot;&quot;
-        class RecordDataProtocol(Protocol):
-            def dataReceived(self, data):
-                self.data = data
-        proto = RecordDataProtocol()
-        factory = ClientFactory()
-        factory.protocol = lambda: proto
-        reactor = MemoryReactor()
-        logged = loggedReactor(reactor)
-        logged.connectTCP('192.168.1.2', 1234, factory, 21, '127.0.0.2')
-        [(host, port, factory, timeout, bindAddress)] = reactor.tcpClients
-        self.assertEqual('192.168.1.2', host)
-        self.assertEqual(1234, port)
-        self.assertIsInstance(factory, _TrafficLoggingFactory)
-        self.assertEqual(21, timeout)
-        self.assertEqual('127.0.0.2', bindAddress)
-
-        # Verify that the factory and protocol specified are really being used
-        protocol = factory.buildProtocol(None)
-        protocol.makeConnection(None)
-        protocol.dataReceived(&quot;foo&quot;)
-        self.assertEqual(proto.data, &quot;foo&quot;)
-
-
-    def test_getLogFiles(self):
-        &quot;&quot;&quot;
-        The reactor returned by L{loggedReactor} has a C{getLogFiles} method
-        which returns a L{logstate} instance containing the active and
-        completed log files tracked by the logging wrapper.
-        &quot;&quot;&quot;
-        wrapped = ClientFactory()
-        wrapped.protocol = Discard
-        reactor = MemoryReactor()
-        logged = loggedReactor(reactor)
-        logged.connectTCP('127.0.0.1', 1234, wrapped)
-        factory = reactor.tcpClients[0][2]
-
-        finished = factory.buildProtocol(None)
-        finished.makeConnection(StringTransport())
-        finished.dataReceived('finished')
-        finished.connectionLost(None)
-
-        active = factory.buildProtocol(None)
-        active.makeConnection(StringTransport())
-        active.dataReceived('active')
-
-        logs = logged.getLogFiles()
-        self.assertEqual(1, len(logs.finished))
-        self.assertIn('finished', logs.finished[0].getvalue())
-        self.assertEqual(1, len(logs.active))
-        self.assertIn('active', logs.active[0].getvalue())
-
-
-
-class TrafficLoggingFactoryTests(TestCase):
-    &quot;&quot;&quot;
-    Tests for L{_TrafficLoggingFactory}.
-    &quot;&quot;&quot;
-    def setUp(self):
-        self.wrapped = ClientFactory()
-        self.wrapped.protocol = Discard
-        self.factory = _TrafficLoggingFactory(self.wrapped)
-
-
-    def test_receivedBytesLogged(self):
-        &quot;&quot;&quot;
-        When bytes are delivered through a protocol created by
-        L{_TrafficLoggingFactory}, they are added to a log kept on that
-        factory.
-        &quot;&quot;&quot;
-        protocol = self.factory.buildProtocol(None)
-
-        # The factory should now have a new StringIO log file
-        self.assertEqual(1, len(self.factory.logs))
-
-        transport = StringTransport()
-        protocol.makeConnection(transport)
-
-        protocol.dataReceived(&quot;hello, world&quot;)
-        self.assertEqual(
-            &quot;*\nC 0: 'hello, world'\n&quot;, self.factory.logs[0].getvalue())
-
-
-    def test_finishedLogs(self):
-        &quot;&quot;&quot;
-        When connections are lost, the corresponding log files are moved into
-        C{_TrafficLoggingFactory.finishedLogs}.
-        &quot;&quot;&quot;
-        protocol = self.factory.buildProtocol(None)
-        transport = StringTransport()
-        protocol.makeConnection(transport)
-        logfile = self.factory.logs[0]
-        protocol.connectionLost(None)
-        self.assertEqual(0, len(self.factory.logs))
-        self.assertEqual([logfile], self.factory.finishedLogs)
-
-
-    def test_finishedLogsLimit(self):
-        &quot;&quot;&quot;
-        Only the most recent C{_TrafficLoggingFactory.LOGFILE_LIMIT} logfiles
-        are kept in C{_TrafficLoggingFactory.finishedLogs}.
-        &quot;&quot;&quot;
-        self.factory.LOGFILE_LIMIT = 2
-        first = self.factory.buildProtocol(None)
-        first.makeConnection(StringTransport())
-        second = self.factory.buildProtocol(None)
-        second.makeConnection(StringTransport())
-        third = self.factory.buildProtocol(None)
-        third.makeConnection(StringTransport())
-
-        second.connectionLost(None)
-        first.connectionLost(None)
-        third.connectionLost(None)
-
-        self.assertEqual(
-            [first.logfile, third.logfile], self.factory.finishedLogs)
</del></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtesttest_webadminpy"></a>
<div class="delfile"><h4>Deleted: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_webadmin.py (15047 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_webadmin.py        2015-08-17 20:38:08 UTC (rev 15047)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/test_webadmin.py        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -1,144 +0,0 @@
</span><del>-##
-# Copyright (c) 2012-2015 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-##
-
-from twisted.trial.unittest import TestCase
-from contrib.performance.loadtest.webadmin import LoadSimAdminResource
-
-class WebAdminTests(TestCase):
-    &quot;&quot;&quot;
-    Tests for L{LoadSimAdminResource}.
-    &quot;&quot;&quot;
-
-    class FakeReporter(object):
-
-        def generateReport(self, output):
-            output.write(&quot;FakeReporter&quot;)
-
-
-    class FakeReactor(object):
-
-        def __init__(self):
-            self.running = True
-
-        def stop(self):
-            self.running = False
-
-
-    class FakeLoadSim(object):
-
-        def __init__(self):
-            self.reactor = WebAdminTests.FakeReactor()
-            self.reporter = WebAdminTests.FakeReporter()
-            self.running = True
-
-        def stop(self):
-            self.running = False
-
-
-    class FakeRequest(object):
-
-        def __init__(self, **kwargs):
-            self.args = kwargs
-
-
-    def test_resourceGET(self):
-        &quot;&quot;&quot;
-        Test render_GET
-        &quot;&quot;&quot;
-
-        loadsim = WebAdminTests.FakeLoadSim()
-        resource = LoadSimAdminResource(loadsim)
-
-        response = resource.render_GET(WebAdminTests.FakeRequest())
-        self.assertTrue(response.startswith(&quot;&lt;html&gt;&quot;))
-        self.assertTrue(response.find(resource.token) != -1)
-
-
-    def test_resourcePOST_Stop(self):
-        &quot;&quot;&quot;
-        Test render_POST when Stop button is clicked
-        &quot;&quot;&quot;
-
-        loadsim = WebAdminTests.FakeLoadSim()
-        resource = LoadSimAdminResource(loadsim)
-        self.assertTrue(loadsim.reactor.running)
-
-        response = resource.render_POST(WebAdminTests.FakeRequest(
-            token=(resource.token,),
-            stop=None,
-        ))
-        self.assertTrue(response.startswith(&quot;&lt;html&gt;&quot;))
-        self.assertTrue(response.find(resource.token) == -1)
-        self.assertTrue(response.find(&quot;FakeReporter&quot;) != -1)
-        self.assertFalse(loadsim.running)
-
-
-    def test_resourcePOST_Stop_BadToken(self):
-        &quot;&quot;&quot;
-        Test render_POST when Stop button is clicked but token is wrong
-        &quot;&quot;&quot;
-
-        loadsim = WebAdminTests.FakeLoadSim()
-        resource = LoadSimAdminResource(loadsim)
-        self.assertTrue(loadsim.reactor.running)
-
-        response = resource.render_POST(WebAdminTests.FakeRequest(
-            token=(&quot;xyz&quot;,),
-            stop=None,
-        ))
-        self.assertTrue(response.startswith(&quot;&lt;html&gt;&quot;))
-        self.assertTrue(response.find(resource.token) != -1)
-        self.assertTrue(response.find(&quot;FakeReporter&quot;) == -1)
-        self.assertTrue(loadsim.running)
-
-
-    def test_resourcePOST_Results(self):
-        &quot;&quot;&quot;
-        Test render_POST when Results button is clicked
-        &quot;&quot;&quot;
-
-        loadsim = WebAdminTests.FakeLoadSim()
-        resource = LoadSimAdminResource(loadsim)
-        self.assertTrue(loadsim.reactor.running)
-
-        response = resource.render_POST(WebAdminTests.FakeRequest(
-            token=(resource.token,),
-            results=None,
-        ))
-        self.assertTrue(response.startswith(&quot;&lt;html&gt;&quot;))
-        self.assertTrue(response.find(resource.token) != -1)
-        self.assertTrue(response.find(&quot;FakeReporter&quot;) != -1)
-        self.assertTrue(loadsim.running)
-
-
-    def test_resourcePOST_Results_BadToken(self):
-        &quot;&quot;&quot;
-        Test render_POST when Results button is clicked and token is wrong
-        &quot;&quot;&quot;
-
-        loadsim = WebAdminTests.FakeLoadSim()
-        resource = LoadSimAdminResource(loadsim)
-        self.assertTrue(loadsim.reactor.running)
-
-        response = resource.render_POST(WebAdminTests.FakeRequest(
-            token=(&quot;xyz&quot;,),
-            results=None,
-        ))
-        self.assertTrue(response.startswith(&quot;&lt;html&gt;&quot;))
-        self.assertTrue(response.find(resource.token) != -1)
-        self.assertTrue(response.find(&quot;FakeReporter&quot;) == -1)
-        self.assertTrue(loadsim.running)
</del></span></pre></div>
<a id="CalendarServerbranchesuserssredmondclientsimcontribperformanceloadtestteststest_distributionspy"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/tests/test_distributions.py (0 => 15048)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/tests/test_distributions.py                                (rev 0)
+++ CalendarServer/branches/users/sredmond/clientsim/contrib/performance/loadtest/tests/test_distributions.py        2015-08-17 21:14:42 UTC (rev 15048)
</span><span class="lines">@@ -0,0 +1,101 @@
</span><ins>+from twisted.trial.unittest import TestCase
+
+from contrib.performance.loadtest.distributions import (
+    LogNormalDistribution, UniformDiscreteDistribution,
+    UniformIntegerDistribution, WorkDistribution, RecurrenceDistribution
+)
+
+from pycalendar.datetime import DateTime
+from pycalendar.timezone import Timezone
+
+class DistributionTests(TestCase):
+    def test_lognormal(self):
+        dist = LogNormalDistribution(mu=1, sigma=1)
+        for _ignore_i in range(100):
+            value = dist.sample()
+            self.assertIsInstance(value, float)
+            self.assertTrue(value &gt;= 0.0, &quot;negative value %r&quot; % (value,))
+            self.assertTrue(value &lt;= 1000, &quot;implausibly high value %r&quot; % (value,))
+
+        dist = LogNormalDistribution(mode=1, median=2)
+        for _ignore_i in range(100):
+            value = dist.sample()
+            self.assertIsInstance(value, float)
+            self.assertTrue(value &gt;= 0.0, &quot;negative value %r&quot; % (value,))
+            self.assertTrue(value &lt;= 1000, &quot;implausibly high value %r&quot; % (value,))
+
+        dist = LogNormalDistribution(mode=1, mean=2)
+        for _ignore_i in range(100):
+            value = dist.sample()
+            self.assertIsInstance(value, float)
+            self.assertTrue(value &gt;= 0.0, &quot;negative value %r&quot; % (value,))
+            self.assertTrue(value &lt;= 1000, &quot;implausibly high value %r&quot; % (value,))
+
+        self.assertRaises(ValueError, LogNormalDistribution, mu=1)
+        self.assertRaises(ValueError, LogNormalDistribution, sigma=1)
+        self.assertRaises(ValueError, LogNormalDistribution, mode=1)
+        self.assertRaises(ValueError, LogNormalDistribution, mean=1)
+        self.assertRaises(ValueError, LogNormalDistribution, median=1)
+
+
+    def test_uniformdiscrete(self):
+        population = [1, 5, 6, 9]
+        counts = dict.fromkeys(population, 0)
+        dist = UniformDiscreteDistribution(population)
+        for _ignore_i in range(len(population) * 10):
+            counts[dist.sample()] += 1
+        self.assertEqual(dict.fromkeys(population, 10), counts)
+
+
+    def test_workdistribution(self):
+        tzname = &quot;US/Eastern&quot;
+        dist = WorkDistribution([&quot;mon&quot;, &quot;wed&quot;, &quot;thu&quot;, &quot;sat&quot;], 10, 20, tzname)
+        dist._helperDistribution = UniformDiscreteDistribution([35 * 60 * 60 + 30 * 60])
+        dist.now = lambda tzname = None: DateTime(2011, 5, 29, 18, 5, 36, tzid=tzname)
+        value = dist.sample()
+        self.assertEqual(
+            # Move past three workdays - monday, wednesday, thursday - using 30
+            # of the hours, and then five and a half hours into the fourth
+            # workday, saturday.  Workday starts at 10am, so the sample value
+            # is 3:30pm, ie 1530 hours.
+            DateTime(2011, 6, 4, 15, 30, 0, tzid=Timezone(tzid=tzname)),
+            value
+        )
+
+        dist = WorkDistribution([&quot;mon&quot;, &quot;tue&quot;, &quot;wed&quot;, &quot;thu&quot;, &quot;fri&quot;], 10, 20, tzname)
+        dist._helperDistribution = UniformDiscreteDistribution([35 * 60 * 60 + 30 * 60])
+        value = dist.sample()
+        self.assertTrue(isinstance(value, DateTime))
+
+    # twisted.trial.unittest.FailTest: not equal:
+    # a = datetime.datetime(2011, 6, 4, 15, 30, tzinfo=&lt;DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD&gt;)
+    # b = datetime.datetime(2011, 6, 4, 19, 30, tzinfo=&lt;DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST&gt;)
+    # test_workdistribution.todo = &quot;Somehow timezones mess this up&quot;
+
+
+    def test_recurrencedistribution(self):
+        dist = RecurrenceDistribution(False)
+        for _ignore in range(100):
+            value = dist.sample()
+            self.assertTrue(value is None)
+
+        dist = RecurrenceDistribution(True, {&quot;daily&quot;: 1, &quot;none&quot;: 2, &quot;weekly&quot;: 1})
+        dist._helperDistribution = UniformDiscreteDistribution([0, 3, 2, 1, 0], randomize=False)
+        value = dist.sample()
+        self.assertTrue(value is not None)
+        value = dist.sample()
+        self.assertTrue(value is None)
+        value = dist.sample()
+        self.assertTrue(value is None)
+        value = dist.sample()
+        self.assertTrue(value is not None)
+        value = dist.sample()
+        self.assertTrue(value is not None)
+
+
+    def test_uniform(self):
+        dist = UniformIntegerDistribution(-5, 10)
+        for _ignore_i in range(100):
+            value = dist.sample()
+            self.assertTrue(-5 &lt;= value &lt; 10)
+            self.assertIsInstance(value, int)
</ins></span></pre>
</div>
</div>

</body>
</html>