<!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>[12016] CalendarServer/branches/users/cdaboo/sharing-in-the-store</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/12016">12016</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2013-12-02 11:04:21 -0800 (Mon, 02 Dec 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>Merge from trunk.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestorebin_calendarserver_preamblepy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/_calendarserver_preamble.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestorecalendarserverpushtesttest_notifierpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/calendarserver/push/test/test_notifier.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestorecalendarservertoolstesttest_calverifypy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/calendarserver/tools/test/test_calverify.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoreconfcaldavdtestplist">CalendarServer/branches/users/cdaboo/sharing-in-the-store/conf/caldavd-test.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoreconfresourcescaldavdresourcesplist">CalendarServer/branches/users/cdaboo/sharing-in-the-store/conf/resources/caldavd-resources.plist</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestorecontribperformanceloadtesticalpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/contrib/performance/loadtest/ical.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoresupportbuildsh">CalendarServer/branches/users/cdaboo/sharing-in-the-store/support/build.sh</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoresupportversionpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/support/version.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretest">CalendarServer/branches/users/cdaboo/sharing-in-the-store/test</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwextinternetsendfdportpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/internet/sendfdport.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwextwhoaggregatepy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/aggregate.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwextwhodirectorypy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/directory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwextwhoindexpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/index.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwextwhotesttest_aggregatepy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_aggregate.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwextwhotesttest_directorypy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_directory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwextwhotesttest_xmlpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_xml.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwextwhoxmlpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/xml.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavicalpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/ical.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavstdconfigpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/stdconfig.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavtesttest_configpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/test/test_config.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavtesttest_icalendarpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/test/test_icalendar.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAfricaCasablancaics">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/Casablanca.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAfricaEl_Aaiunics">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAfricaTripoliics">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/Tripoli.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAmericaEirunepeics">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Eirunepe.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAmericaPorto_Acreics">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Porto_Acre.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAmericaRio_Brancoics">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Rio_Branco.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoBrazilAcreics">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Brazil/Acre.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoLibyaics">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Libya.ics</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfotimezonesxml">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/timezones.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoversiontxt">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/version.txt</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretxdavbasepropertystorebasepy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/base/propertystore/base.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretxdavbasepropertystoretestbasepy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/base/propertystore/test/base.py</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestorebintrial">CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/trial</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestorebintwistd">CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/twistd</a></li>
<li>CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/application/</li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwextprotocolsechopy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/protocols/echo.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwextwhotesttest_indexpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_index.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestoretwistedpluginsmasterchildpy">CalendarServer/branches/users/cdaboo/sharing-in-the-store/twisted/plugins/masterchild.py</a></li>
</ul>

<h3>Property Changed</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboosharinginthestore">CalendarServer/branches/users/cdaboo/sharing-in-the-store/</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserscdaboosharinginthestore"></a>
<div class="propset"><h4>Property changes: CalendarServer/branches/users/cdaboo/sharing-in-the-store</h4>
<pre class="diff"><span>
</span></pre></div>
<a id="svnmergeinfo"></a>
<div class="modfile"><h4>Modified: svn:mergeinfo</h4></div>
<span class="cx">/CalendarServer/branches/config-separation:4379-4443
</span><span class="cx">/CalendarServer/branches/egg-info-351:4589-4625
</span><span class="cx">/CalendarServer/branches/generic-sqlstore:6167-6191
</span><span class="cx">/CalendarServer/branches/new-store:5594-5934
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile:5911-5935
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-5.1-dev:11846
</span><span class="cx">/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
</span><span class="cx">/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
</span><span class="cx">/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
</span><span class="cx">/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
</span><span class="cx">/CalendarServer/branches/users/cdaboo/fix-no-ischedule:11607-11871
</span><span class="cx">/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
</span><span class="cx">/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
</span><span class="cx">/CalendarServer/branches/users/cdaboo/json:11622-11912
</span><span class="cx">/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
</span><span class="cx">/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
</span><span class="cx">/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
</span><span class="cx">/CalendarServer/branches/users/cdaboo/performance-tweaks:11824-11836
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pods:7297-7377
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycard:7227-7237
</span><span class="cx">/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
</span><span class="cx">/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
</span><span class="cx">/CalendarServer/branches/users/cdaboo/reverse-proxy-pods:11875-11900
</span><span class="cx">/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
</span><span class="cx">/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
</span><span class="cx">/CalendarServer/branches/users/cdaboo/timezones:7443-7699
</span><span class="cx">/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
</span><span class="cx">/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
</span><span class="cx">/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
</span><span class="cx">/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
</span><span class="cx">/CalendarServer/branches/users/glyph/conn-limit:6574-6577
</span><span class="cx">/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
</span><span class="cx">/CalendarServer/branches/users/glyph/dalify:6932-7023
</span><span class="cx">/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
</span><span class="cx">/CalendarServer/branches/users/glyph/deploybuild:7563-7572
</span><span class="cx">/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
</span><span class="cx">/CalendarServer/branches/users/glyph/disable-quota:7718-7727
</span><span class="cx">/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
</span><span class="cx">/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
</span><span class="cx">/CalendarServer/branches/users/glyph/hang-fix:11465-11491
</span><span class="cx">/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
</span><span class="cx">/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
</span><span class="cx">/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
</span><span class="cx">/CalendarServer/branches/users/glyph/linux-tests:6893-6900
</span><span class="cx">/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
</span><span class="cx">/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
</span><span class="cx">/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
</span><span class="cx">/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
</span><span class="cx">/CalendarServer/branches/users/glyph/new-export:7444-7485
</span><span class="cx">/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle:7106-7155
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
</span><span class="cx">/CalendarServer/branches/users/glyph/other-html:8062-8091
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
</span><span class="cx">/CalendarServer/branches/users/glyph/q:9560-9688
</span><span class="cx">/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
</span><span class="cx">/CalendarServer/branches/users/glyph/quota:7604-7637
</span><span class="cx">/CalendarServer/branches/users/glyph/sendfdport:5388-5424
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
</span><span class="cx">/CalendarServer/branches/users/glyph/sharedpool:6490-6550
</span><span class="cx">/CalendarServer/branches/users/glyph/sharing-api:9192-9205
</span><span class="cx">/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
</span><span class="cx">/CalendarServer/branches/users/glyph/sql-store:5929-6073
</span><span class="cx">/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
</span><span class="cx">/CalendarServer/branches/users/glyph/subtransactions:7248-7258
</span><span class="cx">/CalendarServer/branches/users/glyph/table-alias:8651-8664
</span><span class="cx">/CalendarServer/branches/users/glyph/uidexport:7673-7676
</span><span class="cx">/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
</span><span class="cx">/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
</span><span class="cx">/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
</span><span class="cx">/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
</span><span class="cx">/CalendarServer/branches/users/glyph/whenNotProposed:11881-11897
</span><span class="cx">/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
</span><span class="cx">/CalendarServer/branches/users/sagen/applepush:8126-8184
</span><span class="cx">/CalendarServer/branches/users/sagen/inboxitems:7380-7381
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources:5032-5051
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
</span><span class="cx">/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
</span><span class="cx">/CalendarServer/branches/users/sagen/resources-2:5084-5093
</span><span class="cx">/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
</span><span class="cx">/CalendarServer/branches/users/wsanchez/transations:5515-5593
</span><span class="cx">/CalendarServer/trunk:11935-11938
</span><span class="cx">   + /CalDAVTester/trunk:11193-11198
</span><span class="cx">/CalendarServer/branches/config-separation:4379-4443
</span><span class="cx">/CalendarServer/branches/egg-info-351:4589-4625
</span><span class="cx">/CalendarServer/branches/generic-sqlstore:6167-6191
</span><span class="cx">/CalendarServer/branches/new-store:5594-5934
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile:5911-5935
</span><span class="cx">/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
</span><span class="cx">/CalendarServer/branches/release/CalendarServer-5.1-dev:11846
</span><span class="cx">/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
</span><span class="cx">/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
</span><span class="cx">/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
</span><span class="cx">/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
</span><span class="cx">/CalendarServer/branches/users/cdaboo/fix-no-ischedule:11607-11871
</span><span class="cx">/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
</span><span class="cx">/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
</span><span class="cx">/CalendarServer/branches/users/cdaboo/json:11622-11912
</span><span class="cx">/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
</span><span class="cx">/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
</span><span class="cx">/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
</span><span class="cx">/CalendarServer/branches/users/cdaboo/performance-tweaks:11824-11836
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pods:7297-7377
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
</span><span class="cx">/CalendarServer/branches/users/cdaboo/pycard:7227-7237
</span><span class="cx">/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
</span><span class="cx">/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
</span><span class="cx">/CalendarServer/branches/users/cdaboo/reverse-proxy-pods:11875-11900
</span><span class="cx">/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
</span><span class="cx">/CalendarServer/branches/users/cdaboo/store-scheduling:10876-11129
</span><span class="cx">/CalendarServer/branches/users/cdaboo/timezones:7443-7699
</span><span class="cx">/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
</span><span class="cx">/CalendarServer/branches/users/gaya/sharedgroups-3:11088-11204
</span><span class="cx">/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
</span><span class="cx">/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
</span><span class="cx">/CalendarServer/branches/users/glyph/conn-limit:6574-6577
</span><span class="cx">/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
</span><span class="cx">/CalendarServer/branches/users/glyph/dalify:6932-7023
</span><span class="cx">/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
</span><span class="cx">/CalendarServer/branches/users/glyph/deploybuild:7563-7572
</span><span class="cx">/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
</span><span class="cx">/CalendarServer/branches/users/glyph/disable-quota:7718-7727
</span><span class="cx">/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
</span><span class="cx">/CalendarServer/branches/users/glyph/enforce-max-requests:11640-11643
</span><span class="cx">/CalendarServer/branches/users/glyph/hang-fix:11465-11491
</span><span class="cx">/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
</span><span class="cx">/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
</span><span class="cx">/CalendarServer/branches/users/glyph/launchd-wrapper-bis:11413-11436
</span><span class="cx">/CalendarServer/branches/users/glyph/linux-tests:6893-6900
</span><span class="cx">/CalendarServer/branches/users/glyph/log-cleanups:11691-11731
</span><span class="cx">/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
</span><span class="cx">/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
</span><span class="cx">/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
</span><span class="cx">/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
</span><span class="cx">/CalendarServer/branches/users/glyph/new-export:7444-7485
</span><span class="cx">/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle:7106-7155
</span><span class="cx">/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
</span><span class="cx">/CalendarServer/branches/users/glyph/other-html:8062-8091
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
</span><span class="cx">/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
</span><span class="cx">/CalendarServer/branches/users/glyph/q:9560-9688
</span><span class="cx">/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
</span><span class="cx">/CalendarServer/branches/users/glyph/quota:7604-7637
</span><span class="cx">/CalendarServer/branches/users/glyph/sendfdport:5388-5424
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
</span><span class="cx">/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
</span><span class="cx">/CalendarServer/branches/users/glyph/sharedpool:6490-6550
</span><span class="cx">/CalendarServer/branches/users/glyph/sharing-api:9192-9205
</span><span class="cx">/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
</span><span class="cx">/CalendarServer/branches/users/glyph/sql-store:5929-6073
</span><span class="cx">/CalendarServer/branches/users/glyph/start-service-start-loop:11060-11065
</span><span class="cx">/CalendarServer/branches/users/glyph/subtransactions:7248-7258
</span><span class="cx">/CalendarServer/branches/users/glyph/table-alias:8651-8664
</span><span class="cx">/CalendarServer/branches/users/glyph/uidexport:7673-7676
</span><span class="cx">/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
</span><span class="cx">/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
</span><span class="cx">/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
</span><span class="cx">/CalendarServer/branches/users/glyph/warning-cleanups:11347-11357
</span><span class="cx">/CalendarServer/branches/users/glyph/whenNotProposed:11881-11897
</span><span class="cx">/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
</span><span class="cx">/CalendarServer/branches/users/sagen/applepush:8126-8184
</span><span class="cx">/CalendarServer/branches/users/sagen/inboxitems:7380-7381
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources:5032-5051
</span><span class="cx">/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
</span><span class="cx">/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
</span><span class="cx">/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
</span><span class="cx">/CalendarServer/branches/users/sagen/resources-2:5084-5093
</span><span class="cx">/CalendarServer/branches/users/sagen/testing:10827-10851,10853-10855
</span><span class="cx">/CalendarServer/branches/users/wsanchez/transations:5515-5593
</span><span class="cx">/CalendarServer/trunk:11935-12015
</span><a id="CalendarServerbranchesuserscdaboosharinginthestorebin_calendarserver_preamblepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/_calendarserver_preamble.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/_calendarserver_preamble.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/_calendarserver_preamble.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -64,6 +64,7 @@
</span><span class="cx">         &quot;calendarserver_manage_postgres&quot;,
</span><span class="cx">         &quot;calendarserver_manage_timezones&quot;,
</span><span class="cx">         &quot;icalendar_split&quot;,
</span><ins>+        &quot;twistd&quot;, &quot;trial&quot;,
</ins><span class="cx">     ]
</span><span class="cx"> 
</span><span class="cx">     if split(sys.argv[0])[-1] not in noConfigOption:
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestorebintrialfromrev12014CalendarServertrunkbintrial"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/trial (from rev 12014, CalendarServer/trunk/bin/trial) (0 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/trial                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/trial        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2013 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.
+##
+
+import sys
+
+#PYTHONPATH
+
+if __name__ == &quot;__main__&quot;:
+    if &quot;PYTHONPATH&quot; in globals():
+        sys.path.insert(0, PYTHONPATH)
+    else:
+        try:
+            import _calendarserver_preamble
+        except ImportError:
+            sys.exc_clear()
+
+    from twisted.scripts.trial import run
+    run()
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestorebintwistdfromrev12014CalendarServertrunkbintwistd"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/twistd (from rev 12014, CalendarServer/trunk/bin/twistd) (0 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/twistd                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/bin/twistd        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2013 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.
+##
+
+import sys
+
+#PYTHONPATH
+
+if __name__ == &quot;__main__&quot;:
+    if &quot;PYTHONPATH&quot; in globals():
+        sys.path.insert(0, PYTHONPATH)
+    else:
+        try:
+            import _calendarserver_preamble
+        except ImportError:
+            sys.exc_clear()
+
+    from twisted.scripts.twistd import run
+    run()
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestorecalendarserverpushtesttest_notifierpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/calendarserver/push/test/test_notifier.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/calendarserver/push/test/test_notifier.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/calendarserver/push/test/test_notifier.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -18,14 +18,13 @@
</span><span class="cx"> from calendarserver.push.notifier import PushDistributor
</span><span class="cx"> from calendarserver.push.notifier import getPubSubAPSConfiguration
</span><span class="cx"> from calendarserver.push.notifier import PushNotificationWork
</span><del>-from twisted.internet.defer import inlineCallbacks, succeed
</del><ins>+from twisted.internet.defer import inlineCallbacks, succeed, gatherResults
</ins><span class="cx"> from twistedcaldav.config import ConfigDict
</span><span class="cx"> from txdav.common.datastore.test.util import populateCalendarsFrom
</span><span class="cx"> from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
</span><span class="cx"> from calendarserver.push.util import PushPriority
</span><span class="cx"> from txdav.idav import ChangeCategory
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> class StubService(object):
</span><span class="cx">     def __init__(self):
</span><span class="cx">         self.reset()
</span><span class="lines">@@ -97,6 +96,7 @@
</span><span class="cx">     def enqueue(self, transaction, pushID, dataChangedTimestamp=None,
</span><span class="cx">         priority=None):
</span><span class="cx">         self.history.append((pushID, priority))
</span><ins>+        return(succeed(None))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -124,27 +124,32 @@
</span><span class="cx"> 
</span><span class="cx">         pushDistributor.reset()
</span><span class="cx">         txn = self._sqlCalendarStore.newTransaction()
</span><del>-        wp = (yield txn.enqueue(PushNotificationWork,
</del><ins>+        proposals = []
+        wp = txn.enqueue(PushNotificationWork,
</ins><span class="cx">             pushID=&quot;/CalDAV/localhost/bar/&quot;,
</span><span class="cx">             priority=PushPriority.high.value
</span><del>-        ))
-        wp = (yield txn.enqueue(PushNotificationWork,
</del><ins>+        )
+        proposals.append(wp.whenExecuted())
+        wp = txn.enqueue(PushNotificationWork,
</ins><span class="cx">             pushID=&quot;/CalDAV/localhost/bar/&quot;,
</span><span class="cx">             priority=PushPriority.high.value
</span><del>-        ))
-        wp = (yield txn.enqueue(PushNotificationWork,
</del><ins>+        )
+        proposals.append(wp.whenExecuted())
+        wp = txn.enqueue(PushNotificationWork,
</ins><span class="cx">             pushID=&quot;/CalDAV/localhost/bar/&quot;,
</span><span class="cx">             priority=PushPriority.high.value
</span><del>-        ))
</del><ins>+        )
+        proposals.append(wp.whenExecuted())
</ins><span class="cx">         # Enqueue a different pushID to ensure those are not grouped with
</span><span class="cx">         # the others:
</span><del>-        wp = (yield txn.enqueue(PushNotificationWork,
</del><ins>+        wp = txn.enqueue(PushNotificationWork,
</ins><span class="cx">             pushID=&quot;/CalDAV/localhost/baz/&quot;,
</span><span class="cx">             priority=PushPriority.high.value
</span><del>-        ))
</del><ins>+        )
+        proposals.append(wp.whenExecuted())
</ins><span class="cx"> 
</span><span class="cx">         yield txn.commit()
</span><del>-        yield wp.whenExecuted()
</del><ins>+        yield gatherResults(proposals)
</ins><span class="cx">         self.assertEquals(set(pushDistributor.history),
</span><span class="cx">             set([(&quot;/CalDAV/localhost/bar/&quot;, PushPriority.high),
</span><span class="cx">              (&quot;/CalDAV/localhost/baz/&quot;, PushPriority.high)]))
</span><span class="lines">@@ -153,20 +158,24 @@
</span><span class="cx">         # enqueuing low, medium, and high notifications
</span><span class="cx">         pushDistributor.reset()
</span><span class="cx">         txn = self._sqlCalendarStore.newTransaction()
</span><del>-        wp = (yield txn.enqueue(PushNotificationWork,
</del><ins>+        proposals = []
+        wp = txn.enqueue(PushNotificationWork,
</ins><span class="cx">             pushID=&quot;/CalDAV/localhost/bar/&quot;,
</span><span class="cx">             priority=PushPriority.low.value
</span><del>-        ))
-        wp = (yield txn.enqueue(PushNotificationWork,
</del><ins>+        )
+        proposals.append(wp.whenExecuted())
+        wp = txn.enqueue(PushNotificationWork,
</ins><span class="cx">             pushID=&quot;/CalDAV/localhost/bar/&quot;,
</span><span class="cx">             priority=PushPriority.high.value
</span><del>-        ))
-        wp = (yield txn.enqueue(PushNotificationWork,
</del><ins>+        )
+        proposals.append(wp.whenExecuted())
+        wp = txn.enqueue(PushNotificationWork,
</ins><span class="cx">             pushID=&quot;/CalDAV/localhost/bar/&quot;,
</span><span class="cx">             priority=PushPriority.medium.value
</span><del>-        ))
</del><ins>+        )
+        proposals.append(wp.whenExecuted())
</ins><span class="cx">         yield txn.commit()
</span><del>-        yield wp.whenExecuted()
</del><ins>+        yield gatherResults(proposals)
</ins><span class="cx">         self.assertEquals(pushDistributor.history,
</span><span class="cx">             [(&quot;/CalDAV/localhost/bar/&quot;, PushPriority.high)])
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestorecalendarservertoolstesttest_calverifypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/calendarserver/tools/test/test_calverify.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/calendarserver/tools/test/test_calverify.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/calendarserver/tools/test/test_calverify.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -511,7 +511,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: True,
</span><span class="lines">@@ -555,7 +555,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: True,
</span><span class="lines">@@ -627,7 +627,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -667,7 +667,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         sync_token_old = (yield (yield self.calendarUnderTest()).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -1418,7 +1418,7 @@
</span><span class="cx">         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name=&quot;calendar&quot;)).syncToken())
</span><span class="cx">         sync_token_old2 = (yield (yield self.calendarUnderTest(home=self.uuid2, name=&quot;calendar&quot;)).syncToken())
</span><span class="cx">         sync_token_old3 = (yield (yield self.calendarUnderTest(home=self.uuid3, name=&quot;calendar&quot;)).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -1485,7 +1485,7 @@
</span><span class="cx">         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name=&quot;calendar&quot;)).syncToken())
</span><span class="cx">         sync_token_old2 = (yield (yield self.calendarUnderTest(home=self.uuid2, name=&quot;calendar&quot;)).syncToken())
</span><span class="cx">         sync_token_old3 = (yield (yield self.calendarUnderTest(home=self.uuid3, name=&quot;calendar&quot;)).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -1576,7 +1576,7 @@
</span><span class="cx">         self.assertNotEqual(sync_token_old3, sync_token_new3)
</span><span class="cx"> 
</span><span class="cx">         # Re-scan after changes to make sure there are no errors
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx">         options[&quot;fix&quot;] = False
</span><span class="cx">         calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="cx">         yield calverify.doAction()
</span><span class="lines">@@ -1694,7 +1694,7 @@
</span><span class="cx"> 
</span><span class="cx">         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name=&quot;calendar&quot;)).syncToken())
</span><span class="cx">         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name=&quot;calendar&quot;)).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -1745,7 +1745,7 @@
</span><span class="cx"> 
</span><span class="cx">         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name=&quot;calendar&quot;)).syncToken())
</span><span class="cx">         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name=&quot;calendar&quot;)).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -1803,7 +1803,7 @@
</span><span class="cx">         self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
</span><span class="cx"> 
</span><span class="cx">         # Re-scan after changes to make sure there are no errors
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx">         options[&quot;fix&quot;] = False
</span><span class="cx">         calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="cx">         yield calverify.doAction()
</span><span class="lines">@@ -1921,7 +1921,7 @@
</span><span class="cx"> 
</span><span class="cx">         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name=&quot;calendar&quot;)).syncToken())
</span><span class="cx">         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name=&quot;calendar&quot;)).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -1970,7 +1970,7 @@
</span><span class="cx"> 
</span><span class="cx">         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name=&quot;calendar&quot;)).syncToken())
</span><span class="cx">         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name=&quot;calendar&quot;)).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -2021,7 +2021,7 @@
</span><span class="cx">         self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
</span><span class="cx"> 
</span><span class="cx">         # Re-scan after changes to make sure there are no errors
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx">         options[&quot;fix&quot;] = False
</span><span class="cx">         options[&quot;uuid&quot;] = CalVerifyMismatchTestsBase.uuidl1
</span><span class="cx">         calverify = SchedulingMismatchService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -2430,7 +2430,7 @@
</span><span class="cx"> 
</span><span class="cx">         sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name=&quot;calendar&quot;)).syncToken())
</span><span class="cx">         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name=&quot;calendar&quot;)).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -2592,7 +2592,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name=&quot;calendar&quot;)).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -2639,7 +2639,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name=&quot;calendar&quot;)).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -2678,7 +2678,7 @@
</span><span class="cx">         self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
</span><span class="cx"> 
</span><span class="cx">         # Re-scan after changes to make sure there are no errors
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx">         options[&quot;fix&quot;] = False
</span><span class="cx">         options[&quot;uuid&quot;] = self.uuidl1
</span><span class="cx">         calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -2698,7 +2698,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name=&quot;calendar&quot;)).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -2737,7 +2737,7 @@
</span><span class="cx">         self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
</span><span class="cx"> 
</span><span class="cx">         # Re-scan after changes to make sure there are no errors
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx">         options[&quot;fix&quot;] = False
</span><span class="cx">         options[&quot;uuid&quot;] = self.uuidl1
</span><span class="cx">         calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
</span><span class="lines">@@ -2757,7 +2757,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name=&quot;calendar&quot;)).syncToken())
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx"> 
</span><span class="cx">         options = {
</span><span class="cx">             &quot;ical&quot;: False,
</span><span class="lines">@@ -2796,7 +2796,7 @@
</span><span class="cx">         self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
</span><span class="cx"> 
</span><span class="cx">         # Re-scan after changes to make sure there are no errors
</span><del>-        self.commit()
</del><ins>+        yield self.commit()
</ins><span class="cx">         options[&quot;fix&quot;] = False
</span><span class="cx">         options[&quot;uuid&quot;] = self.uuidl1
</span><span class="cx">         calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoreconfcaldavdtestplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/conf/caldavd-test.plist (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/conf/caldavd-test.plist        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/conf/caldavd-test.plist        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -895,9 +895,10 @@
</span><span class="cx">     &lt;key&gt;EnableWebAdmin&lt;/key&gt;
</span><span class="cx">     &lt;true/&gt;
</span><span class="cx"> 
</span><del>-    &lt;!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 --&gt;
</del><ins>+    &lt;!-- Support for Content-Encoding compression --&gt;
</ins><span class="cx">     &lt;key&gt;ResponseCompression&lt;/key&gt;
</span><del>-    &lt;false/&gt;
</del><ins>+    &lt;false/&gt;  &lt;!-- Off for testing, as debugging is easier that way. --&gt;
+
</ins><span class="cx">     
</span><span class="cx">     &lt;!-- The retry-after value (in seconds) to return with a 503 error. --&gt;
</span><span class="cx">     &lt;key&gt;HTTPRetryAfter&lt;/key&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoreconfresourcescaldavdresourcesplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/conf/resources/caldavd-resources.plist (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/conf/resources/caldavd-resources.plist        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/conf/resources/caldavd-resources.plist        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -669,7 +669,7 @@
</span><span class="cx">     &lt;key&gt;EnableWebAdmin&lt;/key&gt;
</span><span class="cx">     &lt;true/&gt;
</span><span class="cx"> 
</span><del>-    &lt;!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 --&gt;
</del><ins>+    &lt;!-- Support for Content-Encoding compression --&gt;
</ins><span class="cx">     &lt;key&gt;ResponseCompression&lt;/key&gt;
</span><span class="cx">     &lt;false/&gt;
</span><span class="cx">     
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestorecontribperformanceloadtesticalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/contrib/performance/loadtest/ical.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/contrib/performance/loadtest/ical.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/contrib/performance/loadtest/ical.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -1199,7 +1199,7 @@
</span><span class="cx">         connect(GAIEndpoint(self.reactor, host, port), factory)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def _receivedPush(self, inboundID, dataChangedTimestamp):
</del><ins>+    def _receivedPush(self, inboundID, dataChangedTimestamp, priority=5):
</ins><span class="cx">         for href, id in self.ampPushKeys.iteritems():
</span><span class="cx">             if inboundID == id:
</span><span class="cx">                 self._checkCalendarsForEvents(href, push=True)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoresupportbuildsh"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/support/build.sh (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/support/build.sh        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/support/build.sh        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -40,11 +40,67 @@
</span><span class="cx">   fi;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+# Checks for presence of a C header, optionally with a version comparison.
+# With only a header file name, try to include it, returning nonzero if absent.
+# With 3 params, also attempt a version check, returning nonzero if too old.
+# Param 2 is a minimum acceptable version number
+# Param 3 is a #define from the source that holds the installed version number
+# Examples:
+#   Assert that ldap.h is present
+#     find_header &quot;ldap.h&quot;
+#   Assert that ldap.h is present with a version &gt;= 20344
+#     find_header &quot;ldap.h&quot; 20344 &quot;LDAP_VENDOR_VERSION&quot;
</ins><span class="cx"> find_header () {
</span><del>-  local sysheader=&quot;$1&quot;; shift;
-  echo &quot;#include &lt;${sysheader}&gt;&quot; | cc -x c -c - -o /dev/null 2&gt; /dev/null;
-  return &quot;$?&quot;;
-}
</del><ins>+  ARGS=&quot;$@&quot;;
+  ret=1;  # default to a failed check, forcing a fetch of the depencency
+  i=0;
+  for a in $ARGS; do
+    [ $i -eq 0 ] &amp;&amp; local sysheader=&quot;$1&quot;;
+    [ $i -eq 1 ] &amp;&amp; local minver=&quot;$2&quot;;
+    [ $i -eq 2 ] &amp;&amp; local def=&quot;$3&quot;;
+    i=$(($i+1));
+  done;
+  [ ! $sysheader ] &amp;&amp; return 1;
+  # Check for presence of a header. We use the &quot;-c&quot; cc option because we don't
+  # need to emit a file; cc exits nonzero if it can't find the header
+  if [ $# -lt 2 ]; then
+    echo &quot;#include &lt;${sysheader}&gt;&quot; | cc -x c -c - -o /dev/null 2&gt; /dev/null;
+    return &quot;$?&quot;;
+  # Check for presence of a header of specified version
+  else
+    found='';
+    local aout=$(mktemp -t ccXXXXXX); # compiled executable file path
+    local prog=$(mktemp -t ccXXXXXX); # C source file path
+    cat &lt;&lt;DOC &gt; ${prog}
+#include &lt;${sysheader}&gt;
+#include &lt;stdio.h&gt;
+#define STR(x)   #x
+#define SHOW_DEFINE(x) printf(&quot;%s&quot;, STR(x))
+int main()
+{
+    if (${def})
+    {
+        SHOW_DEFINE(${def});
+        return 0;
+    };
+    return 1;
+};
+DOC
+    cc -x c -o ${aout} ${prog} &amp;&gt; /dev/null;
+    if [ $? -eq 0 ] &amp;&amp; [ -e ${aout} ] ; then
+      found=$(${aout});
+    fi;
+    if [ $? -eq 0 ] &amp;&amp; [ ! -z ${found} ] ; then
+      cmp_version $minver $found;
+      ret=$?;
+    else
+      ret=1;   #cc exited nonzero or didn't emit a file
+    fi;
+    rm -f &quot;${aout}&quot;;
+    rm -f &quot;${prog}&quot;;
+  fi;
+  return $ret;
+};
</ins><span class="cx"> 
</span><span class="cx"> # Initialize all the global state required to use this library.
</span><span class="cx"> init_build () {
</span><span class="lines">@@ -213,10 +269,13 @@
</span><span class="cx">   if &quot;${force_setup}&quot; || [ ! -d &quot;${path}&quot; ]; then
</span><span class="cx">     local ext=&quot;$(echo &quot;${url}&quot; | sed 's|^.*\.\([^.]*\)$|\1|')&quot;;
</span><span class="cx"> 
</span><ins>+    untar () { tar -xvf -; }
+    unzipstream () { tmp=&quot;$(mktemp -t ccsXXXXX)&quot;; cat &gt; &quot;${tmp}&quot;; unzip &quot;${tmp}&quot;; rm &quot;${tmp}&quot;; }
</ins><span class="cx">     case &quot;${ext}&quot; in
</span><del>-      gz|tgz) decompress=&quot;gzip -d -c&quot;; ;;
-      bz2)    decompress=&quot;bzip2 -d -c&quot;; ;;
-      tar)    decompress=&quot;cat&quot;; ;;
</del><ins>+      gz|tgz) decompress=&quot;gzip -d -c&quot;; unpack=&quot;untar&quot;; ;;
+      bz2)    decompress=&quot;bzip2 -d -c&quot;; unpack=&quot;untar&quot;; ;;
+      tar)    decompress=&quot;untar&quot;; unpack=&quot;untar&quot;; ;;
+      zip)    decompress=&quot;cat&quot;; unpack=&quot;unzipstream&quot;; ;;
</ins><span class="cx">       *)
</span><span class="cx">         echo &quot;Error in www_get of URL ${url}: Unknown extension ${ext}&quot;;
</span><span class="cx">         exit 1;
</span><span class="lines">@@ -228,7 +287,7 @@
</span><span class="cx">     if [ -n &quot;${cache_deps}&quot; ] &amp;&amp; [ -n &quot;${hash}&quot; ]; then
</span><span class="cx">       mkdir -p &quot;${cache_deps}&quot;;
</span><span class="cx"> 
</span><del>-      local cache_basename=&quot;${name}-$(echo &quot;${url}&quot; | hash)-$(basename &quot;${url}&quot;)&quot;;
</del><ins>+      local cache_basename=&quot;$(echo ${name} | tr '[ ]' '_')-$(echo &quot;${url}&quot; | hash)-$(basename &quot;${url}&quot;)&quot;;
</ins><span class="cx">       local cache_file=&quot;${cache_deps}/${cache_basename}&quot;;
</span><span class="cx"> 
</span><span class="cx">       check_hash () {
</span><span class="lines">@@ -327,7 +386,7 @@
</span><span class="cx"> 
</span><span class="cx">     rm -rf &quot;${path}&quot;;
</span><span class="cx">     cd &quot;$(dirname &quot;${path}&quot;)&quot;;
</span><del>-    get | ${decompress} | tar -xvf -;
</del><ins>+    get | ${decompress} | ${unpack};
</ins><span class="cx">     apply_patches &quot;${name}&quot; &quot;${path}&quot;;
</span><span class="cx">     cd /;
</span><span class="cx">   fi;
</span><span class="lines">@@ -670,12 +729,12 @@
</span><span class="cx">   if type -P memcached &gt; /dev/null; then
</span><span class="cx">     using_system &quot;memcached&quot;;
</span><span class="cx">   else
</span><del>-    local le=&quot;libevent-2.0.17-stable&quot;;
-    local mc=&quot;memcached-1.4.13&quot;;
-    c_dependency -m &quot;dad64aaaaff16b5fbec25160c06fee9a&quot; \
</del><ins>+    local le=&quot;libevent-2.0.21-stable&quot;;
+    local mc=&quot;memcached-1.4.15&quot;;
+    c_dependency -m &quot;b2405cc9ebf264aa47ff615d9de527a2&quot; \
</ins><span class="cx">       &quot;libevent&quot; &quot;${le}&quot; \
</span><del>-      &quot;https://github.com/downloads/libevent/libevent/${le}.tar.gz&quot;;
-    c_dependency -m &quot;6d18c6d25da945442fcc1187b3b63b7f&quot; \
</del><ins>+      &quot;http://github.com/downloads/libevent/libevent/${le}.tar.gz&quot;;
+    c_dependency -m &quot;36ea966f5a29655be1746bf4949f7f69&quot; \
</ins><span class="cx">       &quot;memcached&quot; &quot;${mc}&quot; \
</span><span class="cx">       &quot;http://memcached.googlecode.com/files/${mc}.tar.gz&quot;;
</span><span class="cx">   fi;
</span><span class="lines">@@ -683,8 +742,9 @@
</span><span class="cx">   if type -P postgres &gt; /dev/null; then
</span><span class="cx">     using_system &quot;Postgres&quot;;
</span><span class="cx">   else
</span><del>-    local pgv=&quot;9.2.4&quot;;
-    local pg=&quot;postgresql-${pgv}&quot;;
</del><ins>+    local v=&quot;9.3.1&quot;;
+    local n=&quot;postgresql&quot;;
+    local p=&quot;${n}-${v}&quot;;
</ins><span class="cx"> 
</span><span class="cx">     if type -P dtrace &gt; /dev/null; then
</span><span class="cx">       local enable_dtrace=&quot;--enable-dtrace&quot;;
</span><span class="lines">@@ -692,19 +752,22 @@
</span><span class="cx">       local enable_dtrace=&quot;&quot;;
</span><span class="cx">     fi;
</span><span class="cx"> 
</span><del>-    c_dependency -m &quot;52df0a9e288f02d7e6e0af89ed4dcfc6&quot; \
-      &quot;PostgreSQL&quot; &quot;${pg}&quot; \
-      &quot;ftp://ftp5.us.postgresql.org/pub/PostgreSQL/source/v${pgv}/${pg}.tar.gz&quot; \
</del><ins>+    c_dependency -m &quot;c003d871f712d4d3895956b028a96e74&quot; \
+      &quot;PostgreSQL&quot; &quot;${p}&quot; \
+      &quot;http://ftp.postgresql.org/pub/source/v${v}/${p}.tar.bz2&quot; \
</ins><span class="cx">       --with-python ${enable_dtrace};
</span><span class="cx">     :;
</span><span class="cx">   fi;
</span><span class="cx"> 
</span><del>-  if find_header ldap.h; then
</del><ins>+  if find_header ldap.h 20428 LDAP_VENDOR_VERSION; then
</ins><span class="cx">     using_system &quot;OpenLDAP&quot;;
</span><span class="cx">   else
</span><del>-    c_dependency -m &quot;ec63f9c2add59f323a0459128846905b&quot; \
-      &quot;OpenLDAP&quot; &quot;openldap-2.4.25&quot; \
-      &quot;http://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-2.4.25.tgz&quot; \
</del><ins>+    local v=&quot;2.4.38&quot;;
+    local n=&quot;openldap&quot;;
+    local p=&quot;${n}-${v}&quot;;
+    c_dependency -m &quot;39831848c731bcaef235a04e0d14412f&quot; \
+      &quot;OpenLDAP&quot; &quot;${p}&quot; \
+      &quot;http://www.openldap.org/software/download/OpenLDAP/${n}-release/${p}.tgz&quot; \
</ins><span class="cx">       --disable-bdb --disable-hdb;
</span><span class="cx">   fi;
</span><span class="cx"> 
</span><span class="lines">@@ -726,24 +789,24 @@
</span><span class="cx"> 
</span><span class="cx">   # Sourceforge mirror hostname.
</span><span class="cx">   local sf=&quot;superb-sea2.dl.sourceforge.net&quot;;
</span><del>-  local st=&quot;setuptools-0.6c11&quot;;
</del><ins>+  local st=&quot;setuptools-1.4&quot;;
</ins><span class="cx">   local pypi=&quot;http://pypi.python.org/packages/source&quot;;
</span><span class="cx"> 
</span><del>-  py_dependency -m &quot;7df2a529a074f613b509fb44feefe74e&quot; \
</del><ins>+  py_dependency -v 1 -m &quot;5710464bc5a61d75f5087f15ce63cfe0&quot; \
</ins><span class="cx">     &quot;setuptools&quot; &quot;setuptools&quot; &quot;${st}&quot; \
</span><span class="cx">     &quot;$pypi/s/setuptools/${st}.tar.gz&quot;;
</span><span class="cx"> 
</span><del>-  local v=&quot;4.0.3&quot;;
</del><ins>+  local v=&quot;4.0.5&quot;;
</ins><span class="cx">   local n=&quot;zope.interface&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -v 4 -m &quot;1ddd308f2c83703accd1696158c300eb&quot; \
</del><ins>+  py_dependency -v 4 -m &quot;caf26025ae1b02da124a58340e423dfe&quot; \
</ins><span class="cx">     &quot;Zope Interface&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><del>-    &quot;http://pypi.python.org/packages/source/z/${n}/${p}.tar.gz&quot;;
</del><ins>+    &quot;http://pypi.python.org/packages/source/z/${n}/${p}.zip&quot;;
</ins><span class="cx"> 
</span><del>-  local v=&quot;0.10&quot;;
</del><ins>+  local v=&quot;0.12&quot;;
</ins><span class="cx">   local n=&quot;pyOpenSSL&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -v 0.9 -m &quot;34db8056ec53ce80c7f5fc58bee9f093&quot; \
</del><ins>+  py_dependency -v 0.12 -m &quot;60a7bbb6160950823eddcbba2cbcb0d6&quot; \
</ins><span class="cx">     &quot;${n}&quot; &quot;OpenSSL&quot; &quot;${p}&quot; \
</span><span class="cx">     &quot;http://pypi.python.org/packages/source/p/${n}/${p}.tar.gz&quot;;
</span><span class="cx"> 
</span><span class="lines">@@ -754,18 +817,18 @@
</span><span class="cx">       &quot;${svn_uri_base}/${n}/trunk&quot;;
</span><span class="cx">   fi;
</span><span class="cx"> 
</span><del>-  local v=&quot;0.6.1&quot;;
</del><ins>+  local v=&quot;0.6.4&quot;;
</ins><span class="cx">   local n=&quot;xattr&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -v 0.5 -r 1038 \
-    &quot;${n}&quot; &quot;${n}&quot; &quot;${n}&quot; \
-    &quot;http://svn.red-bean.com/bob/${n}/releases/${p}/&quot;;
</del><ins>+  py_dependency -v 0.6 -m &quot;1bef31afb7038800f8d5cfa2f4562b37&quot; \
+    &quot;${n}&quot; &quot;${n}&quot; &quot;${p}&quot; \
+    &quot;${pypi}/x/${n}/${n}-${v}.tar.gz&quot;;
</ins><span class="cx"> 
</span><span class="cx">   if [ -n &quot;${ORACLE_HOME:-}&quot; ]; then
</span><del>-    local v=&quot;5.1&quot;;
</del><ins>+    local v=&quot;5.1.2&quot;;
</ins><span class="cx">     local n=&quot;cx_Oracle&quot;;
</span><span class="cx">     local p=&quot;${n}-${v}&quot;;
</span><del>-    py_dependency -v &quot;${v}&quot; -m &quot;d2697493a40c9d46c9b7c1c210b61671&quot; \
</del><ins>+    py_dependency -v &quot;${v}&quot; -m &quot;462f309e00f7bff7100e2077fc43172c&quot; \
</ins><span class="cx">       &quot;${n}&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><span class="cx">       &quot;http://${sf}/project/cx-oracle/${v}/${p}.tar.gz&quot;;
</span><span class="cx">   fi;
</span><span class="lines">@@ -779,10 +842,10 @@
</span><span class="cx"> 
</span><span class="cx">   # Maintenance note: next time the Twisted dependency gets updated, check out
</span><span class="cx">   # twext/patches.py.
</span><del>-  local v=&quot;12.3.0&quot;;
</del><ins>+  local v=&quot;13.2.0&quot;;
</ins><span class="cx">   local n=&quot;Twisted&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -v 12.2 -m &quot;6e289825f3bf5591cfd670874cc0862d&quot; \
</del><ins>+  py_dependency -v 13.2 -m &quot;83fe6c0c911cc1602dbffb036be0ba79&quot; \
</ins><span class="cx">     &quot;${n}&quot; &quot;twisted&quot; &quot;${p}&quot; \
</span><span class="cx">     &quot;${pypi}/T/${n}/${p}.tar.bz2&quot;;
</span><span class="cx"> 
</span><span class="lines">@@ -793,22 +856,22 @@
</span><span class="cx">     &quot;${n}&quot; &quot;dateutil&quot; &quot;${p}&quot; \
</span><span class="cx">     &quot;http://www.labix.org/download/${n}/${p}.tar.gz&quot;;
</span><span class="cx"> 
</span><del>-  local v=&quot;0.6.1&quot;;
</del><ins>+  local v=&quot;1.2.0&quot;;
</ins><span class="cx">   local n=&quot;psutil&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -m &quot;3cfcbfb8525f6e4c70110e44a85e907e&quot; \
</del><ins>+  py_dependency -m &quot;f8ae906249e65db21f17d873ae07e584&quot; \
</ins><span class="cx">     &quot;${n}&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><del>-    &quot;http://${n}.googlecode.com/files/${p}.tar.gz&quot;;
</del><ins>+    &quot;${pypi}/p/${n}/${p}.tar.gz&quot;;
</ins><span class="cx"> 
</span><del>-  local v=&quot;2.3.13&quot;;
</del><ins>+  local v=&quot;2.4.13&quot;;
</ins><span class="cx">   local n=&quot;python-ldap&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -v &quot;${v}&quot; -m &quot;895223d32fa10bbc29aa349bfad59175&quot; \
</del><ins>+  py_dependency -v &quot;${v}&quot; -m &quot;74b7b50267761540451eade44b2049ee&quot; \
</ins><span class="cx">     &quot;Python-LDAP&quot; &quot;ldap&quot; &quot;${p}&quot; \
</span><span class="cx">     &quot;${pypi}/p/${n}/${p}.tar.gz&quot;;
</span><span class="cx"> 
</span><span class="cx">   # XXX actually PyCalendar should be imported in-place.
</span><del>-  py_dependency -fe -i &quot;src&quot; -r 11914 \
</del><ins>+  py_dependency -fe -i &quot;src&quot; -r 11947 \
</ins><span class="cx">     &quot;PyCalendar&quot; &quot;pycalendar&quot; &quot;pycalendar&quot; \
</span><span class="cx">     &quot;${svn_uri_base}/PyCalendar/trunk&quot;;
</span><span class="cx"> 
</span><span class="lines">@@ -840,44 +903,45 @@
</span><span class="cx">     &quot;${svn_uri_base}/CalDAVClientLibrary/trunk&quot;;
</span><span class="cx"> 
</span><span class="cx">   # Can't add &quot;-v 2011g&quot; to args because the version check expects numbers.
</span><ins>+  local v=&quot;2013.8&quot;;
</ins><span class="cx">   local n=&quot;pytz&quot;;
</span><del>-  local p=&quot;${n}-2011n&quot;;
-  py_dependency -m &quot;75ffdc113a4bcca8096ab953df746391&quot; \
</del><ins>+  local p=&quot;${n}-${v}&quot;;
+  py_dependency -m &quot;37750ca749ed3a52523b9682b0b7e381&quot; \
</ins><span class="cx">     &quot;${n}&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><span class="cx">     &quot;${pypi}/p/${n}/${p}.tar.gz&quot;;
</span><span class="cx"> 
</span><del>-  local v=&quot;2.5&quot;;
</del><ins>+  local v=&quot;2.6.1&quot;;
</ins><span class="cx">   local n=&quot;pycrypto&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -v &quot;${v}&quot; -m &quot;783e45d4a1a309e03ab378b00f97b291&quot; \
</del><ins>+  py_dependency -v &quot;${v}&quot; -m &quot;55a61a054aa66812daf5161a0d5d7eda&quot; \
</ins><span class="cx">     &quot;PyCrypto&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><span class="cx">     &quot;http://ftp.dlitz.net/pub/dlitz/crypto/${n}/${p}.tar.gz&quot;;
</span><span class="cx"> 
</span><del>-  local v=&quot;0.1.2&quot;;
</del><ins>+  local v=&quot;0.1.7&quot;;
</ins><span class="cx">   local n=&quot;pyasn1&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -v &quot;${v}&quot; -m &quot;a7c67f5880a16a347a4d3ce445862a47&quot; \
</del><ins>+  py_dependency -v &quot;${v}&quot; -m &quot;2cbd80fcd4c7b1c82180d3d76fee18c8&quot; \
</ins><span class="cx">     &quot;${n}&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><span class="cx">     &quot;${pypi}/p/${n}/${p}.tar.gz&quot;;
</span><span class="cx"> 
</span><del>-  local v=&quot;1.1.6&quot;;
</del><ins>+  local v=&quot;1.1.8&quot;;
</ins><span class="cx">   local n=&quot;setproctitle&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -v &quot;1.0&quot; -m &quot;1e42e43b440214b971f4b33c21eac369&quot; \
</del><ins>+  py_dependency -v &quot;1.0&quot; -m &quot;728f4c8c6031bbe56083a48594027edd&quot; \
</ins><span class="cx">     &quot;${n}&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><span class="cx">     &quot;${pypi}/s/${n}/${p}.tar.gz&quot;;
</span><span class="cx"> 
</span><del>-  local v=&quot;0.6&quot;;
</del><ins>+  local v=&quot;0.8&quot;;
</ins><span class="cx">   local n=&quot;cffi&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -v &quot;0.6&quot; -m &quot;5be33b1ab0247a984d42b27344519337&quot; \
</del><ins>+  py_dependency -v &quot;0.6&quot; -m &quot;e61deb0515311bb42d5d58b9403bc923&quot; \
</ins><span class="cx">     &quot;${n}&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><span class="cx">     &quot;${pypi}/c/${n}/${p}.tar.gz&quot;;
</span><span class="cx"> 
</span><del>-  local v=&quot;2.09.1&quot;;
</del><ins>+  local v=&quot;2.10&quot;;
</ins><span class="cx">   local n=&quot;pycparser&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -v &quot;0.6&quot; -m &quot;74aa075fc28b7c24a4426574d1ac91e0&quot; \
</del><ins>+  py_dependency -v &quot;0.6&quot; -m &quot;d87aed98c8a9f386aa56d365fe4d515f&quot; \
</ins><span class="cx">     &quot;${n}&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><span class="cx">     &quot;${pypi}/p/${n}/${p}.tar.gz&quot;;
</span><span class="cx"> 
</span><span class="lines">@@ -889,21 +953,21 @@
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><span class="cx">   py_dependency -o -m &quot;36407974bd5da2af00bf90ca27feeb44&quot; \
</span><span class="cx">     &quot;Epydoc&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><del>-    &quot;https://pypi.python.org/packages/source/e/${n}/${p}.tar.gz&quot;;
</del><ins>+    &quot;${pypi}/e/${n}/${p}.tar.gz&quot;;
</ins><span class="cx"> 
</span><span class="cx">   local v=&quot;0.10.0&quot;;
</span><span class="cx">   local n=&quot;Nevow&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><span class="cx">   py_dependency -o -m &quot;66dda2ad88f42dea05911add15f4d1b2&quot; \
</span><span class="cx">     &quot;${n}&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><del>-    &quot;https://pypi.python.org/packages/source/N/${n}/${p}.tar.gz&quot;;
</del><ins>+    &quot;${pypi}/N/${n}/${p}.tar.gz&quot;;
</ins><span class="cx"> 
</span><del>-  local v=&quot;0.4&quot;;
</del><ins>+  local v=&quot;0.5b1&quot;;
</ins><span class="cx">   local n=&quot;pydoctor&quot;;
</span><span class="cx">   local p=&quot;${n}-${v}&quot;;
</span><del>-  py_dependency -o -m &quot;b7564e12b5d35d4cb529a2c220b25d3a&quot; \
</del><ins>+  py_dependency -o -m &quot;c4fb33672f37624116cc7a0606f74f28&quot; \
</ins><span class="cx">     &quot;${n}&quot; &quot;${n}&quot; &quot;${p}&quot; \
</span><del>-    &quot;https://pypi.python.org/packages/source/p/${n}/${p}.tar.gz&quot;;
</del><ins>+    &quot;{$pypi}/p/${n}/${p}.tar.gz&quot;;
</ins><span class="cx"> 
</span><span class="cx">   if &quot;${do_setup}&quot;; then
</span><span class="cx">     cd &quot;${caldav}&quot;;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoresupportversionpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/support/version.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/support/version.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/support/version.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -26,7 +26,7 @@
</span><span class="cx">     # Compute the version number.
</span><span class="cx">     #
</span><span class="cx"> 
</span><del>-    base_version = &quot;5.2&quot;
</del><ins>+    base_version = &quot;6.0&quot;
</ins><span class="cx"> 
</span><span class="cx">     branches = tuple(
</span><span class="cx">         branch.format(version=base_version)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretest"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/test (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/test        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/test        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -21,7 +21,7 @@
</span><span class="cx"> 
</span><span class="cx"> wd=&quot;$(cd &quot;$(dirname &quot;$0&quot;)&quot; &amp;&amp; pwd -L)&quot;;
</span><span class="cx"> 
</span><del>-. &quot;${wd}/support/build.sh&quot;;
</del><ins>+#. &quot;${wd}/support/build.sh&quot;;
</ins><span class="cx"> 
</span><span class="cx"> do_setup=&quot;false&quot;;
</span><span class="cx"> do_get=&quot;false&quot;;
</span><span class="lines">@@ -74,9 +74,6 @@
</span><span class="cx"> 
</span><span class="cx"> export PYTHONPATH=&quot;${wd}:${PYTHONPATH:-}&quot;;
</span><span class="cx"> 
</span><del>-dependencies;
-trial=&quot;$(type -p trial)&quot;;
-
</del><span class="cx"> if [ $# -gt 0 ]; then
</span><span class="cx">   test_modules=&quot;$@&quot;;
</span><span class="cx">   flaky=true;
</span><span class="lines">@@ -88,7 +85,7 @@
</span><span class="cx"> find &quot;${wd}&quot; -name \*.pyc -print0 | xargs -0 rm;
</span><span class="cx"> 
</span><span class="cx"> mkdir -p &quot;${wd}/data&quot;;
</span><del>-cd &quot;${wd}&quot; &amp;&amp; &quot;${python}&quot; &quot;${trial}&quot; --temp-directory=&quot;${wd}/data/trial&quot; --rterrors ${reactor} ${random} ${until_fail} ${no_colour} ${coverage} ${numjobs} ${test_modules};
</del><ins>+cd &quot;${wd}&quot; &amp;&amp; &quot;${wd}/bin/trial&quot; --temp-directory=&quot;${wd}/data/trial&quot; --rterrors ${reactor} ${random} ${until_fail} ${no_colour} ${coverage} ${numjobs} ${test_modules};
</ins><span class="cx"> 
</span><span class="cx"> if ${flaky}; then
</span><span class="cx">   echo &quot;&quot;;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwextinternetsendfdportpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/internet/sendfdport.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/internet/sendfdport.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/internet/sendfdport.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -94,8 +94,8 @@
</span><span class="cx">     A socket in the master process pointing at a file descriptor that can be
</span><span class="cx">     used to transmit sockets to a subprocess.
</span><span class="cx"> 
</span><del>-    @ivar skt: the UNIX socket used as the sendmsg() transport.
-    @type skt: L{socket.socket}
</del><ins>+    @ivar outSocket: the UNIX socket used as the sendmsg() transport.
+    @type outSocket: L{socket.socket}
</ins><span class="cx"> 
</span><span class="cx">     @ivar outgoingSocketQueue: an outgoing queue of sockets to send to the
</span><span class="cx">         subprocess, along with their descriptions (strings describing their
</span><span class="lines">@@ -115,12 +115,13 @@
</span><span class="cx">     @type dispatcher: L{InheritedSocketDispatcher}
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-    def __init__(self, dispatcher, skt, status):
</del><ins>+    def __init__(self, dispatcher, inSocket, outSocket, status):
</ins><span class="cx">         FileDescriptor.__init__(self, dispatcher.reactor)
</span><span class="cx">         self.status = status
</span><span class="cx">         self.dispatcher = dispatcher
</span><del>-        self.skt = skt          # XXX needs to be set non-blocking by somebody
-        self.fileno = skt.fileno
</del><ins>+        self.inSocket = inSocket
+        self.outSocket = outSocket   # XXX needs to be set non-blocking by somebody
+        self.fileno = outSocket.fileno
</ins><span class="cx">         self.outgoingSocketQueue = []
</span><span class="cx">         self.pendingCloseSocketQueue = []
</span><span class="cx"> 
</span><span class="lines">@@ -138,7 +139,7 @@
</span><span class="cx">         Receive a status / health message and record it.
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         try:
</span><del>-            data, _ignore_flags, _ignore_ancillary = recvmsg(self.skt.fileno())
</del><ins>+            data, _ignore_flags, _ignore_ancillary = recvmsg(self.outSocket.fileno())
</ins><span class="cx">         except SocketError, se:
</span><span class="cx">             if se.errno not in (EAGAIN, ENOBUFS):
</span><span class="cx">                 raise
</span><span class="lines">@@ -155,7 +156,7 @@
</span><span class="cx">         while self.outgoingSocketQueue:
</span><span class="cx">             skt, desc = self.outgoingSocketQueue.pop(0)
</span><span class="cx">             try:
</span><del>-                sendfd(self.skt.fileno(), skt.fileno(), desc)
</del><ins>+                sendfd(self.outSocket.fileno(), skt.fileno(), desc)
</ins><span class="cx">             except SocketError, se:
</span><span class="cx">                 if se.errno in (EAGAIN, ENOBUFS):
</span><span class="cx">                     self.outgoingSocketQueue.insert(0, (skt, desc))
</span><span class="lines">@@ -341,14 +342,27 @@
</span><span class="cx">         i, o = socketpair()
</span><span class="cx">         i.setblocking(False)
</span><span class="cx">         o.setblocking(False)
</span><del>-        a = _SubprocessSocket(self, o, self.statusWatcher.initialStatus())
</del><ins>+        a = _SubprocessSocket(self, i, o, self.statusWatcher.initialStatus())
</ins><span class="cx">         self._subprocessSockets.append(a)
</span><span class="cx">         if self._isDispatching:
</span><span class="cx">             a.startReading()
</span><span class="cx">         return i
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def removeSocket(self, skt):
+        &quot;&quot;&quot;
+        Removes a previously added socket from the pool of sockets being used
+        for transmitting file descriptors to child processes.
+        &quot;&quot;&quot;
+        for a in self._subprocessSockets:
+            if a.inSocket == skt:
+                self._subprocessSockets.remove(a)
+                break
+        else:
+            raise ValueError(&quot;Unknown socket: {0}&quot;.format(skt))
</ins><span class="cx"> 
</span><ins>+
+
</ins><span class="cx"> class InheritedPort(FileDescriptor, object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     An L{InheritedPort} is an L{IReadDescriptor}/L{IWriteDescriptor} created in
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwextprotocolsechopyfromrev12014CalendarServertrunktwextprotocolsechopy"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/protocols/echo.py (from rev 12014, CalendarServer/trunk/twext/protocols/echo.py) (0 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/protocols/echo.py                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/protocols/echo.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -0,0 +1,35 @@
</span><ins>+##
+# Copyright (c) 2013 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;
+Echo protocol.
+&quot;&quot;&quot;
+
+__all__ = [&quot;EchoProtocol&quot;]
+
+from twisted.internet.protocol import Protocol
+
+
+class EchoProtocol(Protocol):
+    &quot;&quot;&quot;
+    Say what you hear.
+    &quot;&quot;&quot;
+
+    def dataReceived(self, data):
+        &quot;&quot;&quot;
+        As soon as any data is received, write it back.
+        &quot;&quot;&quot;
+        self.transport.write(data)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwextwhoaggregatepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/aggregate.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/aggregate.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/aggregate.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -30,8 +30,8 @@
</span><span class="cx"> 
</span><span class="cx"> from twext.who.idirectory import DirectoryConfigurationError
</span><span class="cx"> from twext.who.idirectory import IDirectoryService
</span><del>-from twext.who.index import DirectoryService as BaseDirectoryService
-from twext.who.index import DirectoryRecord
</del><ins>+from twext.who.directory import DirectoryService as BaseDirectoryService
+from twext.who.directory import DirectoryRecord
</ins><span class="cx"> from twext.who.util import ConstantsContainer
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwextwhodirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/directory.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/directory.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/directory.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -41,7 +41,7 @@
</span><span class="cx"> @implementer(IDirectoryService)
</span><span class="cx"> class DirectoryService(object):
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    Generic implementation of L{IDirectoryService}.
</del><ins>+    Generic (and abstract) implementation of L{IDirectoryService}.
</ins><span class="cx"> 
</span><span class="cx">     Most of the C{recordsWith*} methods call L{recordsWithFieldValue}, which in
</span><span class="cx">     turn calls L{recordsFromExpression} with a corresponding
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwextwhoindexpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/index.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/index.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/index.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -30,7 +30,7 @@
</span><span class="cx"> from twisted.internet.defer import succeed, inlineCallbacks, returnValue
</span><span class="cx"> 
</span><span class="cx"> from twext.who.util import ConstantsContainer
</span><del>-from twext.who.util import describe, uniqueResult
</del><ins>+from twext.who.util import uniqueResult
</ins><span class="cx"> from twext.who.idirectory import FieldName as BaseFieldName
</span><span class="cx"> from twext.who.expression import MatchExpression, MatchType, MatchFlags
</span><span class="cx"> from twext.who.directory import DirectoryService as BaseDirectoryService
</span><span class="lines">@@ -55,7 +55,91 @@
</span><span class="cx"> 
</span><span class="cx"> class DirectoryService(BaseDirectoryService):
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    XML directory service.
</del><ins>+    Generic (and abstract) in-memory-indexed directory service.
+
+    This class implements the record access API in L{BaseDirectoryService} by
+    caching all records in an in-memory dictionary.
+
+    Each indexed field has a top-level key in the index and in turn contains
+    a dictionary in which keys are field values, and values are directory
+    records which have a matching field value for the cooresponding key::
+
+        {
+            &lt;FieldName1&gt;: {
+                &lt;value1a&gt;: set([&lt;record1a1&gt;, ...]),
+                ...
+            },
+            ...
+        }
+
+    Here is an example index for a service with a three user records and one
+    group record::
+
+        {
+            &lt;FieldName=uid&gt;: {
+                u'__calendar-dev__': set([
+                    &lt;DirectoryRecord (group)calendar-dev&gt;
+                ]),
+                u'__dre__': set([
+                    &lt;DirectoryRecord (user)dre&gt;
+                ]),
+                u'__sagen__': set([
+                    &lt;DirectoryRecord (user)sagen&gt;
+                ]),
+                u'__wsanchez__': set([
+                    &lt;DirectoryRecord (user)wsanchez&gt;
+                ])
+            },
+            &lt;FieldName=recordType&gt;: {
+                &lt;RecordType=group&gt;: set([
+                    &lt;DirectoryRecord (group)calendar-dev&gt;,
+                ]),
+                &lt;RecordType=user&gt;: set([
+                    &lt;DirectoryRecord (user)sagen&gt;,
+                    &lt;DirectoryRecord (user)wsanchez&gt;
+                ])
+            },
+            &lt;FieldName=shortNames&gt;: {
+                u'calendar-dev': set([&lt;DirectoryRecord (group)calendar-dev&gt;]),
+                u'dre': set([&lt;DirectoryRecord (user)dre&gt;]),
+                u'sagen': set([&lt;DirectoryRecord (user)sagen&gt;]),
+                u'wilfredo_sanchez': set([&lt;DirectoryRecord (user)wsanchez&gt;]),
+                u'wsanchez': set([&lt;DirectoryRecord (user)wsanchez&gt;])
+            },
+            &lt;FieldName=emailAddresses&gt;: {
+                'dev@bitbucket.calendarserver.org': set([
+                    &lt;DirectoryRecord (group)calendar-dev&gt;
+                ]),
+                'dre@bitbucket.calendarserver.org': set([
+                    &lt;DirectoryRecord (user)dre&gt;
+                ]),
+                'sagen@bitbucket.calendarserver.org': set([
+                    &lt;DirectoryRecord (user)sagen&gt;
+                ]),
+                'shared@example.com': set([
+                    &lt;DirectoryRecord (user)sagen&gt;,
+                    &lt;DirectoryRecord (user)dre&gt;
+                ]),
+                'wsanchez@bitbucket.calendarserver.org': set([
+                    &lt;DirectoryRecord (user)wsanchez&gt;
+                ]),
+                'wsanchez@devnull.twistedmatrix.com': set([
+                    &lt;DirectoryRecord (user)wsanchez&gt;
+                ])
+            },
+            &lt;FieldName=memberUIDs&gt;: {
+                u'__sagen__': set([&lt;DirectoryRecord (group)calendar-dev&gt;]),
+                u'__wsanchez__': set([&lt;DirectoryRecord (group)calendar-dev&gt;])
+            }
+        }
+
+    The field names that are indexed are defined by the C{indexedFields}
+    attribute of the service.
+
+    A subclass must override L{loadRecords}, which populates the index.
+
+    @cvar indexedFields: an iterable of field names (C{NamedConstant})
+        which are indexed.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     fieldName = ConstantsContainer(chain(
</span><span class="lines">@@ -82,43 +166,74 @@
</span><span class="cx">     @property
</span><span class="cx">     def index(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Call L{loadRecords}C{()} and return the index.
</del><ins>+        Call L{loadRecords} and return the index.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         self.loadRecords()
</span><span class="cx">         return self._index
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    @index.setter
-    def index(self, value):
</del><ins>+    def loadRecords(self):
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        Sets the index.
</del><ins>+        Load records.  This method is called by the L{index} property and
+        provides a hook into which the index can be updated.
</ins><span class="cx"> 
</span><del>-        @param index: An index.
-        @type index: L{dict}
</del><ins>+        This method must be implemented by subclasses.
+
+        An example implementation::
+
+            def loadRecords(self):
+                self.flush()
+                while True:
+                    records = readSomeRecordsFromMyBackEnd()
+                    if not records:
+                        break
+                    self.indexRecords(records)
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        self._index = value
</del><ins>+        raise NotImplementedError(&quot;Subclasses must implement loadRecords().&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def loadRecords(self):
</del><ins>+    def indexRecords(self, records):
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        Load records.
</del><ins>+        Add some records to the index.
+
+        @param records: The records to index.
+        @type records: iterable of L{DirectoryRecord}
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        raise NotImplementedError(&quot;Subclasses must implement loadRecords().&quot;)
</del><ins>+        index = self._index
</ins><span class="cx"> 
</span><ins>+        for fieldName in self.indexedFields:
+            index.setdefault(fieldName, {})
</ins><span class="cx"> 
</span><ins>+        for record in records:
+            for fieldName in self.indexedFields:
+                values = record.fields.get(fieldName, None)
+
+                if values is not None:
+                    if not BaseFieldName.isMultiValue(fieldName):
+                        values = (values,)
+
+                    for value in values:
+                        index[fieldName].setdefault(value, set()).add(record)
+
+
</ins><span class="cx">     def flush(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Flush the index.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        self._index = None
</del><ins>+        index = {}
</ins><span class="cx"> 
</span><ins>+        for fieldName in self.indexedFields:
+            index.setdefault(fieldName, {})
</ins><span class="cx"> 
</span><ins>+        self._index = index
+
+
</ins><span class="cx">     def indexedRecordsFromMatchExpression(self, expression, records=None):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Finds records in the internal indexes matching a single expression.
</span><span class="cx"> 
</span><span class="cx">         @param expression: An expression.
</span><del>-        @type expression: L{object}
</del><ins>+        @type expression: L{MatchExpression}
</ins><span class="cx"> 
</span><span class="cx">         @param records: a set of records to limit the search to. C{None} if
</span><span class="cx">             the whole directory should be searched.
</span><span class="lines">@@ -130,7 +245,15 @@
</span><span class="cx">         predicate = MatchFlags.predicator(expression.flags)
</span><span class="cx">         normalize = MatchFlags.normalizer(expression.flags)
</span><span class="cx"> 
</span><del>-        fieldIndex = self.index[expression.fieldName]
</del><ins>+        try:
+            fieldIndex = self.index[expression.fieldName]
+        except KeyError:
+            raise TypeError(
+                &quot;indexedRecordsFromMatchExpression() was passed an &quot;
+                &quot;expression with an unindexed field: {0!r}&quot;
+                .format(expression.fieldName)
+            )
+
</ins><span class="cx">         matchValue = normalize(expression.fieldValue)
</span><span class="cx">         matchType  = expression.matchType
</span><span class="cx"> 
</span><span class="lines">@@ -154,7 +277,7 @@
</span><span class="cx">                 )
</span><span class="cx">         else:
</span><span class="cx">             raise NotImplementedError(
</span><del>-                &quot;Unknown match type: {0}&quot;.format(describe(matchType))
</del><ins>+                &quot;Unknown match type: {0!r}&quot;.format(matchType)
</ins><span class="cx">             )
</span><span class="cx"> 
</span><span class="cx">         matchingRecords = set()
</span><span class="lines">@@ -173,7 +296,7 @@
</span><span class="cx">         Finds records not in the internal indexes matching a single expression.
</span><span class="cx"> 
</span><span class="cx">         @param expression: An expression.
</span><del>-        @type expression: L{object}
</del><ins>+        @type expression: L{MatchExpression}
</ins><span class="cx"> 
</span><span class="cx">         @param records: a set of records to limit the search to. C{None} if
</span><span class="cx">             the whole directory should be searched.
</span><span class="lines">@@ -198,7 +321,7 @@
</span><span class="cx">             match = lambda fieldValue: predicate(fieldValue == matchValue)
</span><span class="cx">         else:
</span><span class="cx">             raise NotImplementedError(
</span><del>-                &quot;Unknown match type: {0}&quot;.format(describe(matchType))
</del><ins>+                &quot;Unknown match type: {0!r}&quot;.format(matchType)
</ins><span class="cx">             )
</span><span class="cx"> 
</span><span class="cx">         result = set()
</span><span class="lines">@@ -223,6 +346,10 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def recordsFromNonCompoundExpression(self, expression, records=None):
</span><ins>+        &quot;&quot;&quot;
+        This implementation can handle L{MatchExpression} expressions; other
+        expressions are passed up to the superclass.
+        &quot;&quot;&quot;
</ins><span class="cx">         if isinstance(expression, MatchExpression):
</span><span class="cx">             if expression.fieldName in self.indexedFields:
</span><span class="cx">                 return self.indexedRecordsFromMatchExpression(
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwextwhotesttest_aggregatepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_aggregate.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_aggregate.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_aggregate.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -22,15 +22,16 @@
</span><span class="cx"> from twisted.trial import unittest
</span><span class="cx"> 
</span><span class="cx"> from twext.who.idirectory import IDirectoryService, DirectoryConfigurationError
</span><del>-from twext.who.aggregate import DirectoryService
</del><ins>+from twext.who.aggregate import DirectoryService, DirectoryRecord
</ins><span class="cx"> from twext.who.util import ConstantsContainer
</span><del>-
</del><span class="cx"> from twext.who.test import test_directory, test_xml
</span><del>-from twext.who.test.test_xml import QueryMixIn, xmlService
-from twext.who.test.test_xml import TestService as XMLTestService
</del><ins>+from twext.who.test.test_xml import (
+    QueryMixIn, xmlService,
+    TestService as XMLTestService,
+    DirectoryServiceConvenienceTestMixIn
+)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> class BaseTest(object):
</span><span class="cx">     def service(self, services=None):
</span><span class="cx">         if services is None:
</span><span class="lines">@@ -53,11 +54,23 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def xmlService(self, xmlData=None, serviceClass=None):
</span><del>-        return xmlService(self.mktemp(), xmlData, serviceClass)
</del><ins>+        return xmlService(
+            self.mktemp(),
+            xmlData=xmlData,
+            serviceClass=serviceClass
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceBaseTest(BaseTest, test_xml.DirectoryServiceBaseTest):
</del><ins>+class DirectoryServiceTest(
+    unittest.TestCase,
+    BaseTest,
+    DirectoryServiceConvenienceTestMixIn,
+    test_directory.BaseDirectoryServiceTest
+):
+    serviceClass = DirectoryService
+    directoryRecordClass = DirectoryRecord
+
</ins><span class="cx">     def test_repr(self):
</span><span class="cx">         service = self.service()
</span><span class="cx">         self.assertEquals(repr(service), &quot;&lt;TestService u'xyzzy'&gt;&quot;)
</span><span class="lines">@@ -73,7 +86,8 @@
</span><span class="cx">     BaseTest,
</span><span class="cx">     test_directory.BaseDirectoryServiceImmutableTest,
</span><span class="cx"> ):
</span><del>-    pass
</del><ins>+    serviceClass = DirectoryService
+    directoryRecordClass = DirectoryRecord
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -86,21 +100,24 @@
</span><span class="cx">             recordType = ConstantsContainer((XMLTestService.recordType.group,))
</span><span class="cx"> 
</span><span class="cx">         usersService = self.xmlService(
</span><del>-            testXMLConfigUsers,
-            UsersDirectoryService
</del><ins>+            xmlData=testXMLConfigUsers,
+            serviceClass=UsersDirectoryService
</ins><span class="cx">         )
</span><span class="cx">         groupsService = self.xmlService(
</span><del>-            testXMLConfigGroups,
-            GroupsDirectoryService
</del><ins>+            xmlData=testXMLConfigGroups,
+            serviceClass=GroupsDirectoryService
</ins><span class="cx">         )
</span><span class="cx"> 
</span><del>-        return BaseTest.service(self, (usersService, groupsService))
</del><ins>+        return BaseTest.service(
+            self,
+            services=(usersService, groupsService)
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> class DirectoryServiceAggregatedBaseTest(
</span><span class="cx">     AggregatedBaseTest,
</span><del>-    DirectoryServiceBaseTest,
</del><ins>+    DirectoryServiceTest,
</ins><span class="cx"> ):
</span><span class="cx">     pass
</span><span class="cx"> 
</span><span class="lines">@@ -126,8 +143,8 @@
</span><span class="cx">     def test_conflictingRecordTypes(self):
</span><span class="cx">         self.assertRaises(
</span><span class="cx">             DirectoryConfigurationError,
</span><del>-            BaseTest.service, self,
-            (self.xmlService(), self.xmlService(testXMLConfigUsers)),
</del><ins>+            self.service,
+            services=(self.xmlService(), self.xmlService(testXMLConfigUsers)),
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwextwhotesttest_directorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_directory.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_directory.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_directory.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -25,7 +25,6 @@
</span><span class="cx"> 
</span><span class="cx"> from twisted.python.constants import Names, NamedConstant
</span><span class="cx"> from twisted.trial import unittest
</span><del>-from twisted.trial.unittest import SkipTest
</del><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx"> from twisted.internet.defer import succeed
</span><span class="cx"> 
</span><span class="lines">@@ -36,17 +35,67 @@
</span><span class="cx"> from twext.who.directory import DirectoryService, DirectoryRecord
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
+class StubDirectoryService(DirectoryService):
+    &quot;&quot;&quot;
+    Stub directory service with some built-in records and an implementation
+    of C{recordsFromNonCompoundExpression}.
+    &quot;&quot;&quot;
+
+    def __init__(self, realmName):
+        DirectoryService.__init__(self, realmName)
+
+        self.records = RecordStorage(self, DirectoryRecord)
+
+
+    def recordsFromExpression(self, expression):
+        self.seenExpressions = []
+
+        return DirectoryService.recordsFromExpression(self, expression)
+
+
+    def recordsFromNonCompoundExpression(self, expression, records=None):
+        &quot;&quot;&quot;
+        This implementation handles three expressions:
+
+        The expression C{u&quot;None&quot;} will match no records.
+
+        The expressions C{u&quot;twistedmatrix.com&quot;} and C{u&quot;calendarserver.org&quot;}
+        will match records that have an email address ending with the
+        given expression.
+        &quot;&quot;&quot;
+        self.seenExpressions.append(expression)
+
+        if expression == u&quot;None&quot;:
+            return succeed([])
+
+        if expression in (u&quot;twistedmatrix.com&quot;, u&quot;calendarserver.org&quot;):
+            result = []
+            for record in self.records:
+                for email in record.emailAddresses:
+                    if email.endswith(expression):
+                        result.append(record)
+                        break
+            return succeed(result)
+
+        return DirectoryService.recordsFromNonCompoundExpression(
+            self, expression, records=records
+        )
+
+
+
</ins><span class="cx"> class ServiceMixIn(object):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     MixIn that sets up a service appropriate for testing.
</span><span class="cx">     &quot;&quot;&quot;
</span><ins>+
</ins><span class="cx">     realmName = u&quot;xyzzy&quot;
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def service(self):
-        if not hasattr(self, &quot;_service&quot;):
-            self._service = DirectoryService(self.realmName)
-        return self._service
</del><ins>+    def service(self, subClass=None):
+        if subClass is None:
+            subClass = self.serviceClass
+        return subClass(self.realmName)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -76,17 +125,20 @@
</span><span class="cx"> 
</span><span class="cx">     def test_repr(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{repr} returns the expected string.
</del><ins>+        L{DirectoryService.repr} returns the expected string.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><del>-        self.assertEquals(repr(service), &quot;&lt;DirectoryService u'xyzzy'&gt;&quot;)
</del><ins>+        self.assertEquals(
+            repr(service),
+            &quot;&lt;{0} u'xyzzy'&gt;&quot;.format(self.serviceClass.__name__)
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_recordTypes(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordTypes} returns the supported set of record types.
-        For L{DirectoryService}, that's the set of constants in the
-        C{recordType} attribute.
</del><ins>+        L{DirectoryService.recordTypes} returns the supported set of record
+        types. For L{DirectoryService}, that's the set of constants in the
+        L{DirectoryService.recordType} attribute.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx">         self.assertEquals(
</span><span class="lines">@@ -97,8 +149,8 @@
</span><span class="cx"> 
</span><span class="cx">     def test_recordsFromNonCompoundExpression_unknownExpression(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsFromNonCompoundExpression} with an unknown expression type
-        fails with L{QueryNotSupportedError}.
</del><ins>+        L{DirectoryService.recordsFromNonCompoundExpression} with an unknown
+        expression type fails with L{QueryNotSupportedError}.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx">         self.assertFailure(
</span><span class="lines">@@ -110,8 +162,8 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_recordsFromNonCompoundExpression_emptyRecords(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsFromNonCompoundExpression} with an unknown expression type
-        and an empty C{records} set returns an empty result.
</del><ins>+        L{DirectoryService.recordsFromNonCompoundExpression} with an unknown
+        expression type and an empty C{records} set returns an empty result.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx">         result = (
</span><span class="lines">@@ -124,12 +176,13 @@
</span><span class="cx"> 
</span><span class="cx">     def test_recordsFromNonCompoundExpression_nonEmptyRecords(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsFromNonCompoundExpression} with an unknown expression type
-        and a non-empty C{records} fails with L{QueryNotSupportedError}.
</del><ins>+        L{DirectoryService.recordsFromNonCompoundExpression} with an unknown
+        expression type and a non-empty C{records} fails with
+        L{QueryNotSupportedError}.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><del>-        wsanchez = DirectoryRecord(
</del><ins>+        wsanchez = self.directoryRecordClass(
</ins><span class="cx">             service,
</span><span class="cx">             {
</span><span class="cx">                 service.fieldName.recordType: service.recordType.user,
</span><span class="lines">@@ -148,8 +201,8 @@
</span><span class="cx"> 
</span><span class="cx">     def test_recordsFromExpression_unknownExpression(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsFromExpression} with an unknown expression type fails with
-        L{QueryNotSupportedError}.
</del><ins>+        L{DirectoryService.recordsFromExpression} with an unknown expression
+        type fails with L{QueryNotSupportedError}.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx">         result = yield(service.recordsFromExpression(object()))
</span><span class="lines">@@ -159,8 +212,8 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_recordsFromExpression_emptyExpression(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsFromExpression} with an unknown expression type and an empty
-        L{CompoundExpression} returns an empty result.
</del><ins>+        L{DirectoryService.recordsFromExpression} with an unknown expression
+        type and an empty L{CompoundExpression} returns an empty result.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><span class="lines">@@ -204,13 +257,22 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceTest(unittest.TestCase, BaseDirectoryServiceTest):
</del><ins>+class DirectoryServiceRecordsFromExpressionTest(
+    unittest.TestCase,
+    ServiceMixIn
+):
+    &quot;&quot;&quot;
+    Tests for L{DirectoryService.recordsFromExpression}.
+    &quot;&quot;&quot;
+    serviceClass = StubDirectoryService
+    directoryRecordClass = DirectoryRecord
+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_recordsFromExpression_single(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsFromExpression} handles a single expression
</del><ins>+        L{DirectoryService.recordsFromExpression} handles a single expression.
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        service = StubDirectoryService()
</del><ins>+        service = self.service()
</ins><span class="cx"> 
</span><span class="cx">         result = yield service.recordsFromExpression(&quot;twistedmatrix.com&quot;)
</span><span class="cx"> 
</span><span class="lines">@@ -228,10 +290,10 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_recordsFromExpression_OR(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsFromExpression} handles a L{CompoundExpression} with
-        L{Operand.OR}.
</del><ins>+        L{DirectoryService.recordsFromExpression} handles a
+        L{CompoundExpression} with L{Operand.OR}.
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        service = StubDirectoryService()
</del><ins>+        service = self.service()
</ins><span class="cx"> 
</span><span class="cx">         result = yield service.recordsFromExpression(
</span><span class="cx">             CompoundExpression(
</span><span class="lines">@@ -260,10 +322,10 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_recordsFromExpression_AND(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsFromExpression} handles a L{CompoundExpression} with
-        L{Operand.AND}.
</del><ins>+        L{DirectoryService.recordsFromExpression} handles a
+        L{CompoundExpression} with L{Operand.AND}.
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        service = StubDirectoryService()
</del><ins>+        service = self.service()
</ins><span class="cx"> 
</span><span class="cx">         result = yield service.recordsFromExpression(
</span><span class="cx">             CompoundExpression(
</span><span class="lines">@@ -287,11 +349,11 @@
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_recordsFromExpression_AND_optimized(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsFromExpression} handles a L{CompoundExpression} with
-        L{Operand.AND}, and when one of the expression matches no records, the
-        subsequent expressions are skipped.
</del><ins>+        L{DirectoryService.recordsFromExpression} handles a
+        L{CompoundExpression} with L{Operand.AND}, and when one of the
+        expression matches no records, the subsequent expressions are skipped.
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        service = StubDirectoryService()
</del><ins>+        service = self.service()
</ins><span class="cx"> 
</span><span class="cx">         result = yield service.recordsFromExpression(
</span><span class="cx">             CompoundExpression(
</span><span class="lines">@@ -317,10 +379,11 @@
</span><span class="cx"> 
</span><span class="cx">     def test_recordsFromExpression_unknownOperand(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsFromExpression} fails with L{QueryNotSupportedError} when
-        given a L{CompoundExpression} with an unknown operand.
</del><ins>+        L{DirectoryService.recordsFromExpression} fails with
+        L{QueryNotSupportedError} when given a L{CompoundExpression} with an
+        unknown operand.
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        service = StubDirectoryService()
</del><ins>+        service = self.service()
</ins><span class="cx"> 
</span><span class="cx">         results = service.recordsFromExpression(
</span><span class="cx">             CompoundExpression(
</span><span class="lines">@@ -335,9 +398,21 @@
</span><span class="cx">         self.assertFailure(results, QueryNotSupportedError)
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+
+class DirectoryServiceConvenienceTest(
+    unittest.TestCase,
+    BaseDirectoryServiceTest
+):
+    &quot;&quot;&quot;
+    Tests for L{DirectoryService} convenience methods.
+    &quot;&quot;&quot;
+    serviceClass = DirectoryService
+    directoryRecordClass = DirectoryRecord
+
+
</ins><span class="cx">     def test_recordWithUID(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordWithUID} fails with L{QueryNotSupportedError}.
</del><ins>+        L{DirectoryService.recordWithUID} fails with L{QueryNotSupportedError}.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><span class="lines">@@ -349,7 +424,8 @@
</span><span class="cx"> 
</span><span class="cx">     def test_recordWithGUID(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordWithGUID} fails with L{QueryNotSupportedError}.
</del><ins>+        L{DirectoryService.recordWithGUID} fails with
+        L{QueryNotSupportedError}.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><span class="lines">@@ -361,7 +437,8 @@
</span><span class="cx"> 
</span><span class="cx">     def test_recordsWithRecordType(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsWithRecordType} fails with L{QueryNotSupportedError}.
</del><ins>+        L{DirectoryService.recordsWithRecordType} fails with
+        L{QueryNotSupportedError}.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><span class="lines">@@ -374,7 +451,8 @@
</span><span class="cx"> 
</span><span class="cx">     def test_recordWithShortName(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordWithShortName} fails with L{QueryNotSupportedError}.
</del><ins>+        L{DirectoryService.recordWithShortName} fails with
+        L{QueryNotSupportedError}.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><span class="lines">@@ -387,7 +465,8 @@
</span><span class="cx"> 
</span><span class="cx">     def test_recordsWithEmailAddress(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{recordsWithEmailAddress} fails with L{QueryNotSupportedError}.
</del><ins>+        L{DirectoryService.recordsWithEmailAddress} fails with
+        L{QueryNotSupportedError}.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><span class="lines">@@ -400,7 +479,7 @@
</span><span class="cx"> 
</span><span class="cx"> class BaseDirectoryServiceImmutableTest(ServiceMixIn):
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    Immutable directory record tests.
</del><ins>+    Tests for immutable directory services.
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">     def test_updateRecordsNotAllowed(self):
</span><span class="lines">@@ -409,7 +488,7 @@
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><del>-        newRecord = DirectoryRecord(
</del><ins>+        newRecord = self.directoryRecordClass(
</ins><span class="cx">             service,
</span><span class="cx">             fields={
</span><span class="cx">                 service.fieldName.uid: u&quot;__plugh__&quot;,
</span><span class="lines">@@ -442,11 +521,19 @@
</span><span class="cx">     unittest.TestCase,
</span><span class="cx">     BaseDirectoryServiceImmutableTest,
</span><span class="cx"> ):
</span><del>-    pass
</del><ins>+    &quot;&quot;&quot;
+    Tests for immutable L{DirectoryService}.
+    &quot;&quot;&quot;
+    serviceClass = DirectoryService
+    directoryRecordClass = DirectoryRecord
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> class BaseDirectoryRecordTest(ServiceMixIn):
</span><ins>+    &quot;&quot;&quot;
+    Tests for directory records.
+    &quot;&quot;&quot;
+
</ins><span class="cx">     fields_wsanchez = {
</span><span class="cx">         FieldName.uid: u&quot;UID:wsanchez&quot;,
</span><span class="cx">         FieldName.recordType: RecordType.user,
</span><span class="lines">@@ -604,7 +691,7 @@
</span><span class="cx"> 
</span><span class="cx">     def test_repr(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{repr} returns the expected string.
</del><ins>+        L{DirectoryRecord.repr} returns the expected string.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         wsanchez = self.makeRecord(self.fields_wsanchez)
</span><span class="cx"> 
</span><span class="lines">@@ -667,7 +754,7 @@
</span><span class="cx"> 
</span><span class="cx">     def test_description(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        C{description} returns the expected string.
</del><ins>+        L{DirectoryRecord.description} returns the expected string.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         sagen = self.makeRecord(self.fields_sagen)
</span><span class="cx"> 
</span><span class="lines">@@ -685,18 +772,20 @@
</span><span class="cx">             sagen.description()
</span><span class="cx">         )
</span><span class="cx"> 
</span><ins>+    test_description.todo = &quot;Intermittent order issues&quot;
</ins><span class="cx"> 
</span><ins>+
</ins><span class="cx">     def test_members_group(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Group members.
</del><ins>+        Group members for group records.
</ins><span class="cx">         &quot;&quot;&quot;
</span><del>-        raise SkipTest(&quot;Subclasses should implement this test.&quot;)
</del><ins>+        raise NotImplementedError(&quot;Subclasses should implement this test.&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_members_nonGroup(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Non-groups have no members.
</del><ins>+        Group members for non-group records.  Non-groups have no members.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         wsanchez = self.makeRecord(self.fields_wsanchez)
</span><span class="cx"> 
</span><span class="lines">@@ -706,55 +795,64 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def test_groups(self):
</del><ins>+    def test_memberships(self):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Group memberships.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        raise SkipTest(&quot;Subclasses should implement this test.&quot;)
</del><ins>+        raise NotImplementedError(&quot;Subclasses should implement this test.&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> class DirectoryRecordTest(unittest.TestCase, BaseDirectoryRecordTest):
</span><ins>+    &quot;&quot;&quot;
+    Tests for L{DirectoryRecord}.
+    &quot;&quot;&quot;
+    serviceClass = DirectoryService
+    directoryRecordClass = DirectoryRecord
+
</ins><span class="cx">     def test_members_group(self):
</span><span class="cx">         staff = self.makeRecord(self.fields_staff)
</span><span class="cx"> 
</span><span class="cx">         self.assertFailure(staff.members(), NotImplementedError)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def test_groups(self):
</del><ins>+    def test_memberships(self):
</ins><span class="cx">         wsanchez = self.makeRecord(self.fields_wsanchez)
</span><span class="cx"> 
</span><span class="cx">         self.assertFailure(wsanchez.groups(), NotImplementedError)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class StubDirectoryService(DirectoryService):
</del><ins>+class RecordStorage(object):
</ins><span class="cx">     &quot;&quot;&quot;
</span><del>-    Stubn directory service with some built-in records and an implementation
-    of C{recordsFromNonCompoundExpression}.
</del><ins>+    Container for directory records.
</ins><span class="cx">     &quot;&quot;&quot;
</span><ins>+    def __init__(self, service, recordClass):
+        self.service = service
+        self.recordClass = recordClass
+        self.records = []
</ins><span class="cx"> 
</span><del>-    def __init__(self):
-        DirectoryService.__init__(self, u&quot;Stub&quot;)
</del><ins>+        self.addDefaultRecords()
</ins><span class="cx"> 
</span><del>-        self.records = []
-        self._addRecords()
</del><span class="cx"> 
</span><del>-
-    def _addRecords(self):
</del><ins>+    def addDefaultRecords(self):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Add a known set of records to this service.
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        self._addUser(
</del><ins>+        self.addUser(
</ins><span class="cx">             shortNames=[u&quot;wsanchez&quot;, u&quot;wilfredo_sanchez&quot;],
</span><del>-            fullNames=[u&quot;Wilfredo S\xe1nchez Vega&quot;],
</del><ins>+            fullNames=[
+                u&quot;Wilfredo S\xe1nchez Vega&quot;,
+                u&quot;Wilfredo Sanchez Vega&quot;,
+                u&quot;Wilfredo Sanchez&quot;,
+            ],
</ins><span class="cx">             emailAddresses=[
</span><span class="cx">                 u&quot;wsanchez@bitbucket.calendarserver.org&quot;,
</span><span class="cx">                 u&quot;wsanchez@devnull.twistedmatrix.com&quot;,
</span><span class="cx">             ],
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        self._addUser(
</del><ins>+        self.addUser(
</ins><span class="cx">             shortNames=[u&quot;glyph&quot;],
</span><span class="cx">             fullNames=[u&quot;Glyph Lefkowitz&quot;],
</span><span class="cx">             emailAddresses=[
</span><span class="lines">@@ -763,7 +861,7 @@
</span><span class="cx">             ],
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        self._addUser(
</del><ins>+        self.addUser(
</ins><span class="cx">             shortNames=[u&quot;sagen&quot;],
</span><span class="cx">             fullNames=[u&quot;Morgen Sagen&quot;],
</span><span class="cx">             emailAddresses=[
</span><span class="lines">@@ -772,7 +870,7 @@
</span><span class="cx">             ],
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        self._addUser(
</del><ins>+        self.addUser(
</ins><span class="cx">             shortNames=[u&quot;cdaboo&quot;],
</span><span class="cx">             fullNames=[u&quot;Cyrus Daboo&quot;],
</span><span class="cx">             emailAddresses=[
</span><span class="lines">@@ -780,7 +878,7 @@
</span><span class="cx">             ],
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        self._addUser(
</del><ins>+        self.addUser(
</ins><span class="cx">             shortNames=[u&quot;dre&quot;],
</span><span class="cx">             fullNames=[u&quot;Andre LaBranche&quot;],
</span><span class="cx">             emailAddresses=[
</span><span class="lines">@@ -789,7 +887,7 @@
</span><span class="cx">             ],
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        self._addUser(
</del><ins>+        self.addUser(
</ins><span class="cx">             shortNames=[u&quot;exarkun&quot;],
</span><span class="cx">             fullNames=[u&quot;Jean-Paul Calderone&quot;],
</span><span class="cx">             emailAddresses=[
</span><span class="lines">@@ -797,7 +895,7 @@
</span><span class="cx">             ],
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        self._addUser(
</del><ins>+        self.addUser(
</ins><span class="cx">             shortNames=[u&quot;dreid&quot;],
</span><span class="cx">             fullNames=[u&quot;David Reid&quot;],
</span><span class="cx">             emailAddresses=[
</span><span class="lines">@@ -805,7 +903,7 @@
</span><span class="cx">             ],
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        self._addUser(
</del><ins>+        self.addUser(
</ins><span class="cx">             shortNames=[u&quot;joe&quot;],
</span><span class="cx">             fullNames=[u&quot;Joe Schmoe&quot;],
</span><span class="cx">             emailAddresses=[
</span><span class="lines">@@ -813,7 +911,7 @@
</span><span class="cx">             ],
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        self._addUser(
</del><ins>+        self.addUser(
</ins><span class="cx">             shortNames=[u&quot;alyssa&quot;],
</span><span class="cx">             fullNames=[u&quot;Alyssa P. Hacker&quot;],
</span><span class="cx">             emailAddresses=[
</span><span class="lines">@@ -822,7 +920,7 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def _addUser(self, shortNames, fullNames, emailAddresses=[]):
</del><ins>+    def addUser(self, shortNames, fullNames, emailAddresses=[]):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         Add a user record with the given field information.
</span><span class="cx"> 
</span><span class="lines">@@ -835,42 +933,24 @@
</span><span class="cx">         @param emailAddresses: Record email addresses.
</span><span class="cx">         @type emailAddresses: L{list} of L{unicode}s
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        self.records.append(DirectoryRecord(self, {
-            self.fieldName.recordType: self.recordType.user,
-            self.fieldName.uid: u&quot;__{0}__&quot;.format(shortNames[0]),
-            self.fieldName.shortNames: shortNames,
-            self.fieldName.fullNames: fullNames,
-            self.fieldName.password: u&quot;&quot;.join(reversed(shortNames[0])),
-            self.fieldName.emailAddresses: emailAddresses,
</del><ins>+        service = self.service
+        fieldName = service.fieldName
+        recordType = service.recordType
+        self.records.append(self.recordClass(self.service, {
+            fieldName.recordType: recordType.user,
+            fieldName.uid: u&quot;__{0}__&quot;.format(shortNames[0]),
+            fieldName.shortNames: shortNames,
+            fieldName.fullNames: fullNames,
+            fieldName.password: u&quot;&quot;.join(reversed(shortNames[0])),
+            fieldName.emailAddresses: emailAddresses,
</ins><span class="cx">         }))
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def recordsFromExpression(self, expression):
-        self.seenExpressions = []
</del><ins>+    def __iter__(self):
+        return iter(self.records)
</ins><span class="cx"> 
</span><del>-        return DirectoryService.recordsFromExpression(self, expression)
</del><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def recordsFromNonCompoundExpression(self, expression, records=None):
-        self.seenExpressions.append(expression)
-
-        if expression == u&quot;None&quot;:
-            return succeed([])
-
-        if expression in (u&quot;twistedmatrix.com&quot;, u&quot;calendarserver.org&quot;):
-            result = []
-            for record in self.records:
-                for email in record.emailAddresses:
-                    if email.endswith(expression):
-                        result.append(record)
-                        break
-            return succeed(result)
-
-        return DirectoryService.recordsFromNonCompoundExpression(
-            self, expression, records=records
-        )
-
-
</del><span class="cx"> class WackyOperand(Names):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Wacky operands.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwextwhotesttest_indexpyfromrev12014CalendarServertrunktwextwhotesttest_indexpy"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_index.py (from rev 12014, CalendarServer/trunk/twext/who/test/test_index.py) (0 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_index.py                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_index.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -0,0 +1,486 @@
</span><ins>+##
+# Copyright (c) 2013 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;
+Indexed directory service base implementation tests.
+&quot;&quot;&quot;
+
+from twisted.trial import unittest
+from twisted.internet.defer import inlineCallbacks, returnValue
+
+from twext.who.idirectory import FieldName as BaseFieldName
+from twext.who.idirectory import QueryNotSupportedError
+from twext.who.expression import MatchExpression, MatchType
+from twext.who.index import DirectoryService, DirectoryRecord
+from twext.who.test import test_directory
+from twext.who.test.test_directory import RecordStorage
+
+
+
+def noLoadDirectoryService(superClass):
+    &quot;&quot;&quot;
+    Creates an indexed directory service that has a no-op implementation of
+    L{DirectoryService.loadRecords}.
+
+    @param superClass: The superclass of the new service.
+    @type superClass: subclass of L{DirectoryService}
+
+    @return: A new directory service class.
+    @rtype: subclass of C{superClass}
+    &quot;&quot;&quot;
+    assert issubclass(superClass, DirectoryService)
+
+    class NoLoadDirectoryService(superClass):
+        def loadRecords(self):
+            pass
+
+        def indexedRecordsFromMatchExpression(self, *args, **kwargs):
+            self._calledIndexed = True
+            return superClass.indexedRecordsFromMatchExpression(
+                self, *args, **kwargs
+            )
+
+        def unIndexedRecordsFromMatchExpression(self, *args, **kwargs):
+            self._calledUnindexed = True
+            return superClass.unIndexedRecordsFromMatchExpression(
+                self, *args, **kwargs
+            )
+
+    return NoLoadDirectoryService
+
+
+class BaseDirectoryServiceTest(test_directory.BaseDirectoryServiceTest):
+    &quot;&quot;&quot;
+    Tests for indexed directory services.
+    &quot;&quot;&quot;
+
+    def noLoadServicePopulated(self):
+        service = self.service(
+            subClass=noLoadDirectoryService(self.serviceClass)
+        )
+
+        records = RecordStorage(service, DirectoryRecord)
+        service.indexRecords(records)
+        service.records = records
+
+        return service
+
+    def test_indexRecords_positive(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.indexRecords} ensures all record data is in the
+        index.
+        &quot;&quot;&quot;
+        service = self.noLoadServicePopulated()
+        index = service.index
+
+        # Verify that the fields that should be indexed are, in fact, indexed
+        # for each record.
+        for record in service.records:
+            for fieldName in service.indexedFields:
+                values = record.fields.get(fieldName, None)
+
+                if values is None:
+                    continue
+
+                if not BaseFieldName.isMultiValue(fieldName):
+                    values = (values,)
+
+                for value in values:
+                    self.assertIn(fieldName, index)
+                    self.assertIn(value, index[fieldName])
+
+                    indexedRecords = index[fieldName][value]
+                    self.assertIn(record, indexedRecords)
+
+
+    def test_indexRecords_negative(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.indexRecords} does not have extra data in the index.
+        &quot;&quot;&quot;
+        service = self.noLoadServicePopulated()
+        index = service.index
+
+        # Verify that all data in the index cooresponds to the records passed
+        # in.
+        for fieldName, fieldIndex in index.iteritems():
+            for fieldValue, records in fieldIndex.iteritems():
+                for record in records:
+                    self.assertIn(fieldName, record.fields)
+                    values = record.fields[fieldName]
+
+                    if not BaseFieldName.isMultiValue(fieldName):
+                        values = (values,)
+
+                    self.assertIn(fieldValue, values)
+
+
+    def test_flush(self):
+        &quot;&quot;&quot;
+        C{flush} empties the index.
+        &quot;&quot;&quot;
+        service = self.noLoadServicePopulated()
+
+        self.assertFalse(emptyIndex(service.index))  # Test the test
+        service.flush()
+        self.assertTrue(emptyIndex(service.index))
+
+
+    @inlineCallbacks
+    def _test_indexedRecordsFromMatchExpression(
+        self, inOut, matchType, fieldName=BaseFieldName.shortNames,
+    ):
+        service = self.noLoadServicePopulated()
+
+        for subString, uids in (inOut):
+            records = yield service.indexedRecordsFromMatchExpression(
+                MatchExpression(
+                    fieldName, subString,
+                    matchType
+                )
+            )
+            self.assertEquals(
+                set((record.uid for record in records)),
+                set(uids)
+            )
+
+
+    def test_indexedRecordsFromMatchExpression_startsWith(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.indexedRecordsFromMatchExpression} with a startsWith
+        expression.
+        &quot;&quot;&quot;
+        return self._test_indexedRecordsFromMatchExpression(
+            (
+                (u&quot;w&quot;, (u&quot;__wsanchez__&quot;,)),           # Duplicates
+                (u&quot;dr&quot;, (u&quot;__dre__&quot;, u&quot;__dreid__&quot;)),  # Multiple
+                (u&quot;sage&quot;, (u&quot;__sagen__&quot;,)),           # Single
+            ),
+            MatchType.startsWith
+        )
+
+
+    def test_indexedRecordsFromMatchExpression_contains(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.indexedRecordsFromMatchExpression} with a contains
+        expression.
+        &quot;&quot;&quot;
+        return self._test_indexedRecordsFromMatchExpression(
+            (
+                (u&quot;sanch&quot;, (u&quot;__wsanchez__&quot;,)),       # Duplicates
+                (u&quot;dr&quot;, (u&quot;__dre__&quot;, u&quot;__dreid__&quot;)),  # Multiple
+                (u&quot;agen&quot;, (u&quot;__sagen__&quot;,)),           # Single
+            ),
+            MatchType.contains
+        )
+
+
+    def test_indexedRecordsFromMatchExpression_equals(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.indexedRecordsFromMatchExpression} with an equals
+        expression.
+        &quot;&quot;&quot;
+        return self._test_indexedRecordsFromMatchExpression(
+            (
+                (u&quot;wsanchez&quot;, (u&quot;__wsanchez__&quot;,)),  # MultiValue
+                (u&quot;dre&quot;, (u&quot;__dre__&quot;,)),            # Single value
+            ),
+            MatchType.equals
+        )
+
+
+    def test_indexedRecordsFromMatchExpression_notIndexed(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.indexedRecordsFromMatchExpression} with an
+        unindexed field name.
+        &quot;&quot;&quot;
+        result = self._test_indexedRecordsFromMatchExpression(
+            (
+                (u&quot;zehcnasw&quot;, (u&quot;__wsanchez__&quot;,)),
+            ),
+            MatchType.equals,
+            fieldName=BaseFieldName.password
+        )
+        self.assertFailure(result, TypeError)
+
+
+    def test_indexedRecordsFromMatchExpression_notMatchExpression(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.indexedRecordsFromMatchExpression} with a
+        non-match expression.
+        &quot;&quot;&quot;
+        result = self._test_indexedRecordsFromMatchExpression(
+            (
+                (u&quot;zehcnasw&quot;, (u&quot;__wsanchez__&quot;,)),
+            ),
+            &quot;Not a match type we know about&quot;
+        )
+        self.assertFailure(result, NotImplementedError)
+
+
+    @inlineCallbacks
+    def _test_unIndexedRecordsFromMatchExpression(
+        self, inOut, matchType, fieldName=BaseFieldName.fullNames,
+    ):
+        service = self.noLoadServicePopulated()
+
+        for subString, uids in (inOut):
+            records = yield service.unIndexedRecordsFromMatchExpression(
+                MatchExpression(
+                    fieldName, subString,
+                    matchType
+                )
+            )
+            self.assertEquals(
+                set((record.uid for record in records)),
+                set(uids)
+            )
+
+
+    def test_unIndexedRecordsFromMatchExpression_startsWith(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.unIndexedRecordsFromMatchExpression} with a
+        startsWith expression.
+        &quot;&quot;&quot;
+        return self._test_unIndexedRecordsFromMatchExpression(
+            (
+                (u&quot;Wilfredo&quot;, (u&quot;__wsanchez__&quot;,)),    # Duplicates
+                (u&quot;A&quot;, (u&quot;__alyssa__&quot;, u&quot;__dre__&quot;)),  # Multiple
+                (u&quot;Andre&quot;, (u&quot;__dre__&quot;,)),            # Single
+            ),
+            MatchType.startsWith
+        )
+
+
+    def test_unIndexedRecordsFromMatchExpression_contains(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.unIndexedRecordsFromMatchExpression} with a contains
+        expression.
+        &quot;&quot;&quot;
+        return self._test_unIndexedRecordsFromMatchExpression(
+            (
+                (u&quot;Sanchez&quot;, (u&quot;__wsanchez__&quot;,)),     # Duplicates
+                (u&quot;A&quot;, (u&quot;__alyssa__&quot;, u&quot;__dre__&quot;)),  # Multiple
+                (u&quot;LaBra&quot;, (u&quot;__dre__&quot;,)),            # Single
+            ),
+            MatchType.contains
+        )
+
+
+    def test_unIndexedRecordsFromMatchExpression_equals(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.unIndexedRecordsFromMatchExpression} with an equals
+        expression.
+        &quot;&quot;&quot;
+        return self._test_unIndexedRecordsFromMatchExpression(
+            (
+                (u&quot;Wilfredo Sanchez&quot;, (u&quot;__wsanchez__&quot;,)),  # MultiValue
+                (u&quot;Andre LaBranche&quot;, (u&quot;__dre__&quot;,)),        # Single value
+            ),
+            MatchType.equals
+        )
+
+
+    def test_unIndexedRecordsFromMatchExpression_indexed(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.unIndexedRecordsFromMatchExpression} with an
+        indexed field name.
+        &quot;&quot;&quot;
+        self._test_unIndexedRecordsFromMatchExpression(
+            (
+                (u&quot;wsanchez&quot;, (u&quot;__wsanchez__&quot;,)),
+            ),
+            MatchType.equals,
+            fieldName=BaseFieldName.shortNames
+        )
+
+
+    def test_unIndexedRecordsFromMatchExpression_notMatchExpression(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.unIndexedRecordsFromMatchExpression} with a
+        non-match expression.
+        &quot;&quot;&quot;
+        result = self._test_unIndexedRecordsFromMatchExpression(
+            (
+                (u&quot;zehcnasw&quot;, (u&quot;__wsanchez__&quot;,)),
+            ),
+            &quot;Not a match type we know about&quot;
+        )
+        self.assertFailure(result, NotImplementedError)
+
+
+    @inlineCallbacks
+    def _test_recordsFromNonCompoundExpression(self, expression):
+        service = self.noLoadServicePopulated()
+        yield service.recordsFromNonCompoundExpression(expression)
+        returnValue(service)
+
+
+    @inlineCallbacks
+    def test_recordsFromNonCompoundExpression_match_indexed(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.recordsFromNonCompoundExpression} with a
+        L{MatchExpression} for an indexed field calls
+        L{DirectoryRecord.indexedRecordsFromMatchExpression}.
+        &quot;&quot;&quot;
+        service = yield self._test_recordsFromNonCompoundExpression(
+            MatchExpression(BaseFieldName.shortNames, u&quot;...&quot;)
+        )
+        self.assertTrue(getattr(service, &quot;_calledIndexed&quot;, False))
+        self.assertFalse(getattr(service, &quot;_calledUnindexed&quot;, False))
+
+
+    @inlineCallbacks
+    def test_recordsFromNonCompoundExpression_match_unindexed(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.recordsFromNonCompoundExpression} with a
+        L{MatchExpression} for an unindexed field calls
+        L{DirectoryRecord.unIndexedRecordsFromMatchExpression}.
+        &quot;&quot;&quot;
+        service = yield self._test_recordsFromNonCompoundExpression(
+            MatchExpression(BaseFieldName.password, u&quot;...&quot;)
+        )
+        self.assertFalse(getattr(service, &quot;_calledIndexed&quot;, False))
+        self.assertTrue(getattr(service, &quot;_calledUnindexed&quot;, False))
+
+
+    def test_recordsFromNonCompoundExpression_unknown(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.recordsFromNonCompoundExpression} with a
+        an unknown expression calls superclass, which will result in a
+        L{QueryNotSupportedError}.
+        &quot;&quot;&quot;
+        result = self._test_recordsFromNonCompoundExpression(object())
+        self.assertFailure(result, QueryNotSupportedError)
+
+
+
+class DirectoryServiceTest(unittest.TestCase, BaseDirectoryServiceTest):
+    &quot;&quot;&quot;
+    Tests for L{DirectoryService}.
+    &quot;&quot;&quot;
+    serviceClass = DirectoryService
+    directoryRecordClass = DirectoryRecord
+
+
+    def test_init_noIndex(self):
+        &quot;&quot;&quot;
+        Index starts as C{None}.
+        &quot;&quot;&quot;
+        service = self.service()
+        self.assertTrue(emptyIndex(service._index))
+
+
+    def test_index_get(self):
+        &quot;&quot;&quot;
+        Getting the C{index} property calls C{loadRecords}.
+        &quot;&quot;&quot;
+        class TestService(DirectoryService):
+            loaded = False
+
+            def loadRecords(self):
+                self.loaded = True
+
+        service = TestService(u&quot;&quot;)
+        service.index
+        self.assertTrue(service.loaded)
+
+
+    def test_loadRecords(self):
+        &quot;&quot;&quot;
+        L{DirectoryService.loadRecords} raises C{NotImplementedError}.
+        &quot;&quot;&quot;
+        service = self.service()
+        self.assertRaises(NotImplementedError, service.loadRecords)
+
+
+    def _noop(self):
+        &quot;&quot;&quot;
+        Does nothing.
+        &quot;&quot;&quot;
+
+
+    test_recordWithUID = _noop
+    test_recordWithGUID = _noop
+    test_recordsWithRecordType = _noop
+    test_recordWithShortName = _noop
+    test_recordsWithEmailAddress = _noop
+
+
+
+class BaseDirectoryServiceImmutableTest(
+    test_directory.BaseDirectoryServiceImmutableTest
+):
+    &quot;&quot;&quot;
+    Tests for immutable indexed directory services.
+    &quot;&quot;&quot;
+
+
+
+class DirectoryServiceImmutableTest(
+    unittest.TestCase, BaseDirectoryServiceImmutableTest
+):
+    &quot;&quot;&quot;
+    Tests for immutable L{DirectoryService}.
+    &quot;&quot;&quot;
+    serviceClass = DirectoryService
+    directoryRecordClass = DirectoryRecord
+
+
+
+class BaseDirectoryRecordTest(test_directory.BaseDirectoryRecordTest):
+    &quot;&quot;&quot;
+    Tests for indexed directory records.
+    &quot;&quot;&quot;
+
+
+
+class DirectoryRecordTest(unittest.TestCase, BaseDirectoryRecordTest):
+    &quot;&quot;&quot;
+    Tests for L{DirectoryRecord}.
+    &quot;&quot;&quot;
+    serviceClass = DirectoryService
+    directoryRecordClass = DirectoryRecord
+
+
+    def _noop(self):
+        &quot;&quot;&quot;
+        Does nothing.
+        &quot;&quot;&quot;
+
+
+    test_members_group = _noop
+    test_memberships = _noop
+
+
+
+def emptyIndex(index):
+    &quot;&quot;&quot;
+    Determine whether an index is empty.
+
+    @param index: An index.
+    @type index: L{dict}
+
+    @return: true if C{index} is empty, otherwise false.
+    &quot;&quot;&quot;
+    if not index:
+        return True
+
+    for fieldName, fieldIndex in index.iteritems():
+        for fieldValue, records in fieldIndex.iteritems():
+            for record in records:
+                return False
+
+    return True
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwextwhotesttest_xmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_xml.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_xml.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/test/test_xml.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -31,16 +31,19 @@
</span><span class="cx"> from twext.who.expression import MatchExpression, MatchType, MatchFlags
</span><span class="cx"> from twext.who.xml import ParseError
</span><span class="cx"> from twext.who.xml import DirectoryService, DirectoryRecord
</span><ins>+from twext.who.test import test_index
</ins><span class="cx"> 
</span><del>-from twext.who.test import test_directory
</del><span class="cx"> 
</span><span class="cx"> 
</span><ins>+class BaseTest(object):
+    def service(self, subClass=None, xmlData=None):
+        return xmlService(
+            self.mktemp(),
+            xmlData=xmlData,
+            serviceClass=subClass
+        )
</ins><span class="cx"> 
</span><del>-class BaseTest(unittest.TestCase):
-    def service(self, xmlData=None):
-        return xmlService(self.mktemp(), xmlData)
</del><span class="cx"> 
</span><del>-
</del><span class="cx">     def assertRecords(self, records, uids):
</span><span class="cx">         self.assertEquals(
</span><span class="cx">             frozenset((record.uid for record in records)),
</span><span class="lines">@@ -49,18 +52,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceBaseTest(
-    BaseTest,
-    test_directory.BaseDirectoryServiceTest,
-):
-    def test_repr(self):
-        service = self.service()
-
-        self.assertEquals(repr(service), &quot;&lt;TestService (not loaded)&gt;&quot;)
-        service.loadRecords()
-        self.assertEquals(repr(service), &quot;&lt;TestService u'xyzzy'&gt;&quot;)
-
-
</del><ins>+class DirectoryServiceConvenienceTestMixIn(BaseTest):
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_recordWithUID(self):
</span><span class="cx">         service = self.service()
</span><span class="lines">@@ -176,7 +168,24 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceRealmTest(BaseTest):
</del><ins>+class DirectoryServiceTest(
+    unittest.TestCase,
+    DirectoryServiceConvenienceTestMixIn,
+    test_index.BaseDirectoryServiceTest,
+):
+    serviceClass = DirectoryService
+    directoryRecordClass = DirectoryRecord
+
+    def test_repr(self):
+        service = self.service()
+
+        self.assertEquals(repr(service), &quot;&lt;TestService (not loaded)&gt;&quot;)
+        service.loadRecords()
+        self.assertEquals(repr(service), &quot;&lt;TestService u'xyzzy'&gt;&quot;)
+
+
+
+class DirectoryServiceRealmTest(unittest.TestCase, BaseTest):
</ins><span class="cx">     def test_realmNameImmutable(self):
</span><span class="cx">         def setRealmName():
</span><span class="cx">             service = self.service()
</span><span class="lines">@@ -186,7 +195,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceParsingTest(BaseTest):
</del><ins>+class DirectoryServiceParsingTest(unittest.TestCase, BaseTest):
</ins><span class="cx">     def test_reloadInterval(self):
</span><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><span class="lines">@@ -232,7 +241,7 @@
</span><span class="cx">         except ParseError as e:
</span><span class="cx">             self.assertTrue(str(e).startswith(&quot;Incorrect root element&quot;), e)
</span><span class="cx">         else:
</span><del>-            raise AssertionError
</del><ins>+            raise AssertionError(&quot;Expected ParseError&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_noRealmName(self):
</span><span class="lines">@@ -250,7 +259,7 @@
</span><span class="cx">         except ParseError as e:
</span><span class="cx">             self.assertTrue(str(e).startswith(&quot;No realm name&quot;), e)
</span><span class="cx">         else:
</span><del>-            raise AssertionError
</del><ins>+            raise AssertionError(&quot;Expected ParseError&quot;)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def test_unknownFieldElementsClean(self):
</span><span class="lines">@@ -301,7 +310,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceQueryTest(BaseTest):
</del><ins>+class DirectoryServiceQueryTest(unittest.TestCase, BaseTest):
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_queryAnd(self):
</span><span class="cx">         service = self.service()
</span><span class="lines">@@ -663,7 +672,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryServiceMutableTest(BaseTest):
</del><ins>+class DirectoryServiceMutableTest(unittest.TestCase, BaseTest):
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def test_updateRecord(self):
</span><span class="cx">         service = self.service()
</span><span class="lines">@@ -699,7 +708,7 @@
</span><span class="cx">         newRecord = DirectoryRecord(
</span><span class="cx">             service,
</span><span class="cx">             fields={
</span><del>-                service.fieldName.uid:        u&quot;__plugh__&quot;,
</del><ins>+                service.fieldName.uid: u&quot;__plugh__&quot;,
</ins><span class="cx">                 service.fieldName.recordType: service.recordType.user,
</span><span class="cx">                 service.fieldName.shortNames: (u&quot;plugh&quot;,),
</span><span class="cx">             }
</span><span class="lines">@@ -723,7 +732,7 @@
</span><span class="cx">         newRecord = DirectoryRecord(
</span><span class="cx">             service,
</span><span class="cx">             fields={
</span><del>-                service.fieldName.uid:        u&quot;__plugh__&quot;,
</del><ins>+                service.fieldName.uid: u&quot;__plugh__&quot;,
</ins><span class="cx">                 service.fieldName.recordType: service.recordType.user,
</span><span class="cx">                 service.fieldName.shortNames: (u&quot;plugh&quot;,),
</span><span class="cx">             }
</span><span class="lines">@@ -756,9 +765,16 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-class DirectoryRecordTest(BaseTest, test_directory.BaseDirectoryRecordTest):
</del><ins>+class DirectoryRecordTest(
+    unittest.TestCase,
+    BaseTest,
+    test_index.BaseDirectoryRecordTest
+):
+    serviceClass = DirectoryService
+    directoryRecordClass = DirectoryRecord
+
</ins><span class="cx">     @inlineCallbacks
</span><del>-    def test_members(self):
</del><ins>+    def test_members_group(self):
</ins><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><span class="cx">         record = (yield service.recordWithUID(u&quot;__wsanchez__&quot;))
</span><span class="lines">@@ -790,7 +806,7 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def test_groups(self):
</del><ins>+    def test_memberships(self):
</ins><span class="cx">         service = self.service()
</span><span class="cx"> 
</span><span class="cx">         record = (yield service.recordWithUID(u&quot;__wsanchez__&quot;))
</span><span class="lines">@@ -832,7 +848,13 @@
</span><span class="cx">     filePath = FilePath(tmp)
</span><span class="cx">     filePath.setContent(xmlData)
</span><span class="cx"> 
</span><del>-    return serviceClass(filePath)
</del><ins>+    try:
+        return serviceClass(filePath)
+    except Exception as e:
+        raise AssertionError(
+            &quot;Unable to instantiate XML service {0}: {1}&quot;
+            .format(serviceClass, e)
+        )
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwextwhoxmlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/xml.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/xml.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twext/who/xml.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -242,7 +242,7 @@
</span><span class="cx">         if not realmName:
</span><span class="cx">             raise ParseError(&quot;No realm name.&quot;)
</span><span class="cx"> 
</span><del>-        unknownRecordTypes   = set()
</del><ins>+        unknownRecordTypes = set()
</ins><span class="cx">         unknownFieldElements = set()
</span><span class="cx"> 
</span><span class="cx">         records = set()
</span><span class="lines">@@ -259,32 +259,17 @@
</span><span class="cx">         # Store results
</span><span class="cx">         #
</span><span class="cx"> 
</span><del>-        index = {}
</del><ins>+        self.flush()
+        self.indexRecords(records)
</ins><span class="cx"> 
</span><del>-        for fieldName in self.indexedFields:
-            index[fieldName] = {}
-
-        for record in records:
-            for fieldName in self.indexedFields:
-                values = record.fields.get(fieldName, None)
-
-                if values is not None:
-                    if not BaseFieldName.isMultiValue(fieldName):
-                        values = (values,)
-
-                    for value in values:
-                        index[fieldName].setdefault(value, set()).add(record)
-
</del><span class="cx">         self._realmName = realmName
</span><span class="cx"> 
</span><del>-        self._unknownRecordTypes   = unknownRecordTypes
</del><ins>+        self._unknownRecordTypes = unknownRecordTypes
</ins><span class="cx">         self._unknownFieldElements = unknownFieldElements
</span><span class="cx"> 
</span><span class="cx">         self._cacheTag = cacheTag
</span><span class="cx">         self._lastRefresh = now
</span><span class="cx"> 
</span><del>-        self.index = index
-
</del><span class="cx">         return etree
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -348,11 +333,11 @@
</span><span class="cx">     def flush(self):
</span><span class="cx">         BaseDirectoryService.flush(self)
</span><span class="cx"> 
</span><del>-        self._realmName            = None
-        self._unknownRecordTypes   = None
</del><ins>+        self._realmName = None
+        self._unknownRecordTypes = None
</ins><span class="cx">         self._unknownFieldElements = None
</span><del>-        self._cacheTag             = None
-        self._lastRefresh          = 0
</del><ins>+        self._cacheTag = None
+        self._lastRefresh = 0
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def updateRecords(self, records, create=False):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedpluginsmasterchildpyfromrev12014CalendarServertrunktwistedpluginsmasterchildpy"></a>
<div class="copfile"><h4>Copied: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twisted/plugins/masterchild.py (from rev 12014, CalendarServer/trunk/twisted/plugins/masterchild.py) (0 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twisted/plugins/masterchild.py                                (rev 0)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twisted/plugins/masterchild.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -0,0 +1,58 @@
</span><ins>+##
+# Copyright (c) 2010-2013 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 implementer
+
+from twisted.python.reflect import namedClass
+from twisted.plugin import IPlugin
+from twisted.application.service import IServiceMaker
+
+from twext.application.masterchild import MasterOptions, ChildOptions
+
+
+@implementer(IPlugin, IServiceMaker)
+class ServiceMaker(object):
+    def __init__(self, name, description, options, serviceMakerClass):
+        self.tapname = name
+        self.description = description
+        self.options = options
+        self.serviceMakerClass = serviceMakerClass
+        self._serviceMaker = None
+
+
+    def makeService(self, options):
+        if self._serviceMaker is None:
+            self._serviceMaker = namedClass(self.serviceMakerClass)()
+
+        return self._serviceMaker.makeService(options)
+
+
+
+masterServiceMaker = ServiceMaker(
+    &quot;master&quot;,
+    &quot;Master process application container&quot;,
+    MasterOptions,
+    &quot;twext.application.masterchild.MasterServiceMaker&quot;
+)
+
+
+
+childServiceMaker = ServiceMaker(
+    &quot;child&quot;,
+    &quot;Child process application container&quot;,
+    ChildOptions,
+    &quot;twext.application.masterchild.ChildServiceMaker&quot;
+)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavicalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/ical.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/ical.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/ical.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -286,8 +286,7 @@
</span><span class="cx"> 
</span><span class="cx">     def parameterValue(self, name, default=None):
</span><span class="cx">         &quot;&quot;&quot;
</span><del>-        Returns a single value for the given parameter.  Raises
-        InvalidICalendarDataError if the parameter has more than one value.
</del><ins>+        Returns a single value for the given parameter.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         try:
</span><span class="cx">             return self._pycalendar.getParameterValue(name)
</span><span class="lines">@@ -295,6 +294,16 @@
</span><span class="cx">             return default
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def parameterValues(self, name, default=None):
+        &quot;&quot;&quot;
+        Returns a multi-value C{list} for the given parameter.
+        &quot;&quot;&quot;
+        try:
+            return self._pycalendar.getParameterValues(name)
+        except KeyError:
+            return default
+
+
</ins><span class="cx">     def hasParameter(self, paramname):
</span><span class="cx">         return self._pycalendar.hasParameter(paramname)
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavstdconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/stdconfig.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/stdconfig.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/stdconfig.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -304,7 +304,7 @@
</span><span class="cx">                                     # the master process, rather than having
</span><span class="cx">                                     # each client make its connections directly.
</span><span class="cx"> 
</span><del>-    &quot;FailIfUpgradeNeeded&quot;  : True, # Set to True to prevent the server or utility tools
</del><ins>+    &quot;FailIfUpgradeNeeded&quot;  : True, # Set to True to prevent the server or utility
</ins><span class="cx">                                    # tools from running if the database needs a schema
</span><span class="cx">                                    # upgrade.
</span><span class="cx">     &quot;StopAfterUpgradeTriggerFile&quot; : &quot;stop_after_upgrade&quot;,   # if this file exists in ConfigRoot, stop
</span><span class="lines">@@ -892,7 +892,8 @@
</span><span class="cx"> 
</span><span class="cx">     # Support for Content-Encoding compression options as specified in
</span><span class="cx">     # RFC2616 Section 3.5
</span><del>-    &quot;ResponseCompression&quot;: True,
</del><ins>+    # Defaults off, because it weakens TLS (CRIME attack).
+    &quot;ResponseCompression&quot;: False,
</ins><span class="cx"> 
</span><span class="cx">     # The retry-after value (in seconds) to return with a 503 error
</span><span class="cx">     &quot;HTTPRetryAfter&quot;: 180,
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavtesttest_configpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/test/test_config.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/test/test_config.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/test/test_config.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -34,7 +34,7 @@
</span><span class="cx"> &lt;dict&gt;
</span><span class="cx"> 
</span><span class="cx">   &lt;key&gt;ResponseCompression&lt;/key&gt;
</span><del>-  &lt;false/&gt;
</del><ins>+  &lt;true/&gt;
</ins><span class="cx"> 
</span><span class="cx">   &lt;key&gt;HTTPPort&lt;/key&gt;
</span><span class="cx">   &lt;integer&gt;8008&lt;/integer&gt;
</span><span class="lines">@@ -73,7 +73,7 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> def _testResponseCompression(testCase):
</span><del>-    testCase.assertEquals(config.ResponseCompression, False)
</del><ins>+    testCase.assertEquals(config.ResponseCompression, True)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -114,19 +114,19 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def testLoadConfig(self):
</span><del>-        self.assertEquals(config.ResponseCompression, True)
</del><ins>+        self.assertEquals(config.ResponseCompression, False)
</ins><span class="cx"> 
</span><span class="cx">         config.load(self.testConfig)
</span><span class="cx"> 
</span><del>-        self.assertEquals(config.ResponseCompression, False)
</del><ins>+        self.assertEquals(config.ResponseCompression, True)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def testScoping(self):
</span><del>-        self.assertEquals(config.ResponseCompression, True)
</del><ins>+        self.assertEquals(config.ResponseCompression, False)
</ins><span class="cx"> 
</span><span class="cx">         config.load(self.testConfig)
</span><span class="cx"> 
</span><del>-        self.assertEquals(config.ResponseCompression, False)
</del><ins>+        self.assertEquals(config.ResponseCompression, True)
</ins><span class="cx"> 
</span><span class="cx">         _testResponseCompression(self)
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavtesttest_icalendarpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/test/test_icalendar.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/test/test_icalendar.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/test/test_icalendar.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -21,7 +21,7 @@
</span><span class="cx"> from twisted.trial.unittest import SkipTest
</span><span class="cx"> 
</span><span class="cx"> from twistedcaldav.ical import Component, Property, InvalidICalendarDataError, \
</span><del>-    normalizeCUAddress
</del><ins>+    normalizeCUAddress, normalize_iCalStr
</ins><span class="cx"> from twistedcaldav.instance import InvalidOverriddenInstanceError
</span><span class="cx"> import twistedcaldav.test.util
</span><span class="cx"> 
</span><span class="lines">@@ -1152,6 +1152,82 @@
</span><span class="cx">             self.assertEqual(result, str(component).replace(&quot;\r&quot;, &quot;&quot;))
</span><span class="cx"> 
</span><span class="cx"> 
</span><ins>+    def test_parameter_multi_values(self):
+        caldata = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user01@example.com
+ATTENDEE;MEMBER=&quot;urn:uuid:group01&quot;,&quot;urn:uuid:group02&quot;;PARTSTAT=NEEDS-ACTION:mailto:user02@example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01@example.com
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;
+
+        caldata2 = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user01@example.com
+ATTENDEE;MEMBER=&quot;urn:uuid:group01&quot;,&quot;urn:uuid:group02&quot;,&quot;urn:uuid:group03&quot;;PARTSTAT=NEEDS-ACTION:mailto:user02@example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01@example.com
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;
+
+        caldata3 = &quot;&quot;&quot;BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890-1
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user01@example.com
+ATTENDEE;MEMBER=&quot;urn:uuid:group01&quot;;PARTSTAT=NEEDS-ACTION:mailto:user02@example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01@example.com
+END:VEVENT
+END:VCALENDAR
+&quot;&quot;&quot;
+
+        component = Component.fromString(caldata)
+        attendee = component.masterComponent().getAttendeeProperty([&quot;mailto:user02@example.com&quot;, ])
+        self.assertTrue(attendee is not None)
+
+        # Single value retrieved as multi-value
+        partstat = attendee.parameterValues(&quot;PARTSTAT&quot;)
+        self.assertEqual(partstat, [&quot;NEEDS-ACTION&quot;])
+
+        # Multi-value retrieved as single-value
+        member = attendee.parameterValue(&quot;MEMBER&quot;)
+        self.assertEqual(member, &quot;urn:uuid:group01&quot;)
+
+        # Multi-value retrieved as multi-value
+        members = attendee.parameterValues(&quot;MEMBER&quot;)
+        self.assertEqual(members, [&quot;urn:uuid:group01&quot;, &quot;urn:uuid:group02&quot;])
+
+        # Multi-value add a new value
+        members = attendee.parameterValues(&quot;MEMBER&quot;)
+        members.append(&quot;urn:uuid:group03&quot;)
+        attendee.setParameter(&quot;MEMBER&quot;, members)
+        members = attendee.parameterValues(&quot;MEMBER&quot;)
+        self.assertEqual(members, [&quot;urn:uuid:group01&quot;, &quot;urn:uuid:group02&quot;, &quot;urn:uuid:group03&quot;])
+        self.assertEqual(normalize_iCalStr(str(component)), normalize_iCalStr(caldata2))
+
+        # Multi-value back to one
+        members = attendee.parameterValues(&quot;MEMBER&quot;)
+        del members[1:]
+        attendee.setParameter(&quot;MEMBER&quot;, members)
+        members = attendee.parameterValues(&quot;MEMBER&quot;)
+        self.assertEqual(members, [&quot;urn:uuid:group01&quot;])
+        self.assertEqual(normalize_iCalStr(str(component)), normalize_iCalStr(caldata3))
+
+
</ins><span class="cx">     def test_add_property_with_valuetype(self):
</span><span class="cx">         data = &quot;&quot;&quot;BEGIN:VCALENDAR
</span><span class="cx"> VERSION:2.0
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAfricaCasablancaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/Casablanca.ics (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/Casablanca.ics        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/Casablanca.ics        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -54,6 +54,7 @@
</span><span class="cx"> RDATE:20100808T000000
</span><span class="cx"> RDATE:20110731T000000
</span><span class="cx"> RDATE:20120720T030000
</span><ins>+RDATE:20120930T030000
</ins><span class="cx"> RDATE:20130707T030000
</span><span class="cx"> RDATE:20140629T030000
</span><span class="cx"> RDATE:20150618T030000
</span><span class="lines">@@ -61,6 +62,9 @@
</span><span class="cx"> RDATE:20170527T030000
</span><span class="cx"> RDATE:20180516T030000
</span><span class="cx"> RDATE:20190506T030000
</span><ins>+RDATE:20200424T030000
+RDATE:20210413T030000
+RDATE:20220403T030000
</ins><span class="cx"> TZNAME:WET
</span><span class="cx"> TZOFFSETFROM:+0100
</span><span class="cx"> TZOFFSETTO:+0000
</span><span class="lines">@@ -88,17 +92,24 @@
</span><span class="cx"> END:STANDARD
</span><span class="cx"> BEGIN:DAYLIGHT
</span><span class="cx"> DTSTART:20120429T020000
</span><del>-RRULE:FREQ=YEARLY;UNTIL=20190428T020000Z;BYDAY=-1SU;BYMONTH=4
</del><ins>+RRULE:FREQ=YEARLY;UNTIL=20130428T020000Z;BYDAY=-1SU;BYMONTH=4
</ins><span class="cx"> TZNAME:WEST
</span><span class="cx"> TZOFFSETFROM:+0000
</span><span class="cx"> TZOFFSETTO:+0100
</span><span class="cx"> END:DAYLIGHT
</span><span class="cx"> BEGIN:STANDARD
</span><del>-DTSTART:20120930T030000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=9
</del><ins>+DTSTART:20131027T030000
+RRULE:FREQ=YEARLY;UNTIL=20221030T020000Z;BYDAY=-1SU;BYMONTH=10
</ins><span class="cx"> TZNAME:WET
</span><span class="cx"> TZOFFSETFROM:+0100
</span><span class="cx"> TZOFFSETTO:+0000
</span><span class="cx"> END:STANDARD
</span><ins>+BEGIN:DAYLIGHT
+DTSTART:20140330T020000
+RRULE:FREQ=YEARLY;UNTIL=20220327T020000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
</ins><span class="cx"> END:VTIMEZONE
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAfricaEl_Aaiunics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/El_Aaiun.ics        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -15,9 +15,84 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19760414T000000
</span><span class="cx"> RDATE:19760414T000000
</span><del>-TZNAME:WET
</del><ins>+TZNAME:WEST
</ins><span class="cx"> TZOFFSETFROM:-0100
</span><span class="cx"> TZOFFSETTO:+0000
</span><span class="cx"> END:STANDARD
</span><ins>+BEGIN:DAYLIGHT
+DTSTART:19760501T000000
+RRULE:FREQ=YEARLY;UNTIL=19770501T000000Z;BYMONTH=5
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19760801T000000
+RDATE:19760801T000000
+RDATE:19770928T000000
+RDATE:19780804T000000
+RDATE:20080901T000000
+RDATE:20090821T000000
+RDATE:20100808T000000
+RDATE:20110731T000000
+RDATE:20120720T030000
+RDATE:20120930T030000
+RDATE:20130707T030000
+RDATE:20140629T030000
+RDATE:20150618T030000
+RDATE:20160607T030000
+RDATE:20170527T030000
+RDATE:20180516T030000
+RDATE:20190506T030000
+RDATE:20200424T030000
+RDATE:20210413T030000
+RDATE:20220403T030000
+TZNAME:WET
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0000
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19780601T000000
+RDATE:19780601T000000
+RDATE:20080601T000000
+RDATE:20090601T000000
+RDATE:20100502T000000
+RDATE:20110403T000000
+RDATE:20120820T020000
+RDATE:20130810T020000
+RDATE:20140729T020000
+RDATE:20150718T020000
+RDATE:20160707T020000
+RDATE:20170626T020000
+RDATE:20180615T020000
+RDATE:20190605T020000
+RDATE:20200524T020000
+RDATE:20210513T020000
+RDATE:20220503T020000
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+DTSTART:20120429T020000
+RRULE:FREQ=YEARLY;UNTIL=20130428T020000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20131027T030000
+RRULE:FREQ=YEARLY;UNTIL=20221030T020000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:WET
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0000
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:20140330T020000
+RRULE:FREQ=YEARLY;UNTIL=20220327T020000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:WEST
+TZOFFSETFROM:+0000
+TZOFFSETTO:+0100
+END:DAYLIGHT
</ins><span class="cx"> END:VTIMEZONE
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAfricaTripoliics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/Tripoli.ics (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/Tripoli.ics        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Africa/Tripoli.ics        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -20,6 +20,7 @@
</span><span class="cx"> RDATE:19850406T000000
</span><span class="cx"> RDATE:19860404T000000
</span><span class="cx"> RDATE:19970404T000000
</span><ins>+RDATE:20130329T010000
</ins><span class="cx"> TZNAME:CEST
</span><span class="cx"> TZOFFSETFROM:+0100
</span><span class="cx"> TZOFFSETTO:+0200
</span><span class="lines">@@ -82,23 +83,10 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19971004T000000
</span><span class="cx"> RDATE:19971004T000000
</span><ins>+RDATE:20131025T020000
</ins><span class="cx"> TZNAME:EET
</span><span class="cx"> TZOFFSETFROM:+0200
</span><span class="cx"> TZOFFSETTO:+0200
</span><span class="cx"> END:STANDARD
</span><del>-BEGIN:DAYLIGHT
-DTSTART:20130329T010000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=3
-TZNAME:CEST
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20131025T020000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=10
-TZNAME:CET
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-END:STANDARD
</del><span class="cx"> END:VTIMEZONE
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAmericaEirunepeics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Eirunepe.ics (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Eirunepe.ics        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Eirunepe.ics        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -51,6 +51,7 @@
</span><span class="cx"> RDATE:19870214T000000
</span><span class="cx"> RDATE:19880207T000000
</span><span class="cx"> RDATE:19940220T000000
</span><ins>+RDATE:20131110T000000
</ins><span class="cx"> TZNAME:ACT
</span><span class="cx"> TZOFFSETFROM:-0400
</span><span class="cx"> TZOFFSETTO:-0500
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAmericaPorto_Acreics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Porto_Acre.ics (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Porto_Acre.ics        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Porto_Acre.ics        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -49,6 +49,7 @@
</span><span class="cx"> RDATE:19860315T000000
</span><span class="cx"> RDATE:19870214T000000
</span><span class="cx"> RDATE:19880207T000000
</span><ins>+RDATE:20131110T000000
</ins><span class="cx"> TZNAME:ACT
</span><span class="cx"> TZOFFSETFROM:-0400
</span><span class="cx"> TZOFFSETTO:-0500
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoAmericaRio_Brancoics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Rio_Branco.ics (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Rio_Branco.ics        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/America/Rio_Branco.ics        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -49,6 +49,7 @@
</span><span class="cx"> RDATE:19860315T000000
</span><span class="cx"> RDATE:19870214T000000
</span><span class="cx"> RDATE:19880207T000000
</span><ins>+RDATE:20131110T000000
</ins><span class="cx"> TZNAME:ACT
</span><span class="cx"> TZOFFSETFROM:-0400
</span><span class="cx"> TZOFFSETTO:-0500
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoBrazilAcreics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Brazil/Acre.ics (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Brazil/Acre.ics        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Brazil/Acre.ics        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -49,6 +49,7 @@
</span><span class="cx"> RDATE:19860315T000000
</span><span class="cx"> RDATE:19870214T000000
</span><span class="cx"> RDATE:19880207T000000
</span><ins>+RDATE:20131110T000000
</ins><span class="cx"> TZNAME:ACT
</span><span class="cx"> TZOFFSETFROM:-0400
</span><span class="cx"> TZOFFSETTO:-0500
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoLibyaics"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Libya.ics (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Libya.ics        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/Libya.ics        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -20,6 +20,7 @@
</span><span class="cx"> RDATE:19850406T000000
</span><span class="cx"> RDATE:19860404T000000
</span><span class="cx"> RDATE:19970404T000000
</span><ins>+RDATE:20130329T010000
</ins><span class="cx"> TZNAME:CEST
</span><span class="cx"> TZOFFSETFROM:+0100
</span><span class="cx"> TZOFFSETTO:+0200
</span><span class="lines">@@ -82,23 +83,10 @@
</span><span class="cx"> BEGIN:STANDARD
</span><span class="cx"> DTSTART:19971004T000000
</span><span class="cx"> RDATE:19971004T000000
</span><ins>+RDATE:20131025T020000
</ins><span class="cx"> TZNAME:EET
</span><span class="cx"> TZOFFSETFROM:+0200
</span><span class="cx"> TZOFFSETTO:+0200
</span><span class="cx"> END:STANDARD
</span><del>-BEGIN:DAYLIGHT
-DTSTART:20130329T010000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=3
-TZNAME:CEST
-TZOFFSETFROM:+0100
-TZOFFSETTO:+0200
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20131025T020000
-RRULE:FREQ=YEARLY;BYDAY=-1FR;BYMONTH=10
-TZNAME:CET
-TZOFFSETFROM:+0200
-TZOFFSETTO:+0100
-END:STANDARD
</del><span class="cx"> END:VTIMEZONE
</span><span class="cx"> END:VCALENDAR
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfotimezonesxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/timezones.xml (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/timezones.xml        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/timezones.xml        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -2,7 +2,7 @@
</span><span class="cx"> &lt;!DOCTYPE timezones SYSTEM &quot;timezones.dtd&quot;&gt;
</span><span class="cx"> 
</span><span class="cx"> &lt;timezones&gt;
</span><del>-  &lt;dtstamp&gt;2013-10-01T01:19:11Z&lt;/dtstamp&gt;
</del><ins>+  &lt;dtstamp&gt;2013-11-15T16:10:31Z&lt;/dtstamp&gt;
</ins><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;Africa/Abidjan&lt;/tzid&gt;
</span><span class="cx">     &lt;dtstamp&gt;2011-10-05T11:50:21Z&lt;/dtstamp&gt;
</span><span class="lines">@@ -78,8 +78,8 @@
</span><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;Africa/Casablanca&lt;/tzid&gt;
</span><del>-    &lt;dtstamp&gt;2013-07-11T02:11:45Z&lt;/dtstamp&gt;
-    &lt;md5&gt;b4e345b053c4699911078dcd16854bab&lt;/md5&gt;
</del><ins>+    &lt;dtstamp&gt;2013-11-15T16:10:31Z&lt;/dtstamp&gt;
+    &lt;md5&gt;4f58dbcb4f7e6dfa7af3d710aeff97b4&lt;/md5&gt;
</ins><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;Africa/Ceuta&lt;/tzid&gt;
</span><span class="lines">@@ -113,8 +113,8 @@
</span><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;Africa/El_Aaiun&lt;/tzid&gt;
</span><del>-    &lt;dtstamp&gt;2011-10-05T11:50:21Z&lt;/dtstamp&gt;
-    &lt;md5&gt;494500808e8542fd83e4706654c43545&lt;/md5&gt;
</del><ins>+    &lt;dtstamp&gt;2013-11-15T16:10:31Z&lt;/dtstamp&gt;
+    &lt;md5&gt;7933d45461e9f988ebfc9d062ce894e4&lt;/md5&gt;
</ins><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;Africa/Freetown&lt;/tzid&gt;
</span><span class="lines">@@ -264,9 +264,9 @@
</span><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;Africa/Tripoli&lt;/tzid&gt;
</span><del>-    &lt;dtstamp&gt;2013-01-14T15:32:16Z&lt;/dtstamp&gt;
</del><ins>+    &lt;dtstamp&gt;2013-11-15T16:10:31Z&lt;/dtstamp&gt;
</ins><span class="cx">     &lt;alias&gt;Libya&lt;/alias&gt;
</span><del>-    &lt;md5&gt;f59e5f16eec995c112b8b27580922fdd&lt;/md5&gt;
</del><ins>+    &lt;md5&gt;6e8040bfd898654905bfd0a49c64e365&lt;/md5&gt;
</ins><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;Africa/Tunis&lt;/tzid&gt;
</span><span class="lines">@@ -570,8 +570,8 @@
</span><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;America/Eirunepe&lt;/tzid&gt;
</span><del>-    &lt;dtstamp&gt;2011-10-05T11:50:21Z&lt;/dtstamp&gt;
-    &lt;md5&gt;d3c4df84162ebac445e1c2dcdb81f3b2&lt;/md5&gt;
</del><ins>+    &lt;dtstamp&gt;2013-11-15T16:10:31Z&lt;/dtstamp&gt;
+    &lt;md5&gt;cc34b66260adda629311a54df088470b&lt;/md5&gt;
</ins><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;America/El_Salvador&lt;/tzid&gt;
</span><span class="lines">@@ -964,8 +964,8 @@
</span><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;America/Porto_Acre&lt;/tzid&gt;
</span><del>-    &lt;dtstamp&gt;2011-10-05T11:50:21Z&lt;/dtstamp&gt;
-    &lt;md5&gt;ce88b8461b7217e1d9ec8f4dfac34670&lt;/md5&gt;
</del><ins>+    &lt;dtstamp&gt;2013-11-15T16:10:31Z&lt;/dtstamp&gt;
+    &lt;md5&gt;b2b04e3c9c1dab12a3764b99dd8536fc&lt;/md5&gt;
</ins><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;America/Porto_Velho&lt;/tzid&gt;
</span><span class="lines">@@ -1006,10 +1006,10 @@
</span><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;America/Rio_Branco&lt;/tzid&gt;
</span><del>-    &lt;dtstamp&gt;2011-10-05T11:50:21Z&lt;/dtstamp&gt;
</del><ins>+    &lt;dtstamp&gt;2013-11-15T16:10:31Z&lt;/dtstamp&gt;
</ins><span class="cx">     &lt;alias&gt;America/Porto_Acre&lt;/alias&gt;
</span><span class="cx">     &lt;alias&gt;Brazil/Acre&lt;/alias&gt;
</span><del>-    &lt;md5&gt;c41ff8b67906037ce014d42019e5831f&lt;/md5&gt;
</del><ins>+    &lt;md5&gt;51273c4521cd00a8ecdcff336f0c9503&lt;/md5&gt;
</ins><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;America/Rosario&lt;/tzid&gt;
</span><span class="lines">@@ -1885,8 +1885,8 @@
</span><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;Brazil/Acre&lt;/tzid&gt;
</span><del>-    &lt;dtstamp&gt;2011-10-05T11:50:21Z&lt;/dtstamp&gt;
-    &lt;md5&gt;b034140cdfc442b0f989f6a6dd6ec620&lt;/md5&gt;
</del><ins>+    &lt;dtstamp&gt;2013-11-15T16:10:31Z&lt;/dtstamp&gt;
+    &lt;md5&gt;ad33124888f5c5dd9bed9317ef0bb953&lt;/md5&gt;
</ins><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;Brazil/DeNoronha&lt;/tzid&gt;
</span><span class="lines">@@ -2641,8 +2641,8 @@
</span><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;Libya&lt;/tzid&gt;
</span><del>-    &lt;dtstamp&gt;2013-01-14T15:32:16Z&lt;/dtstamp&gt;
-    &lt;md5&gt;f514b497bd861c14aa21612f9101c50c&lt;/md5&gt;
</del><ins>+    &lt;dtstamp&gt;2013-11-15T16:10:31Z&lt;/dtstamp&gt;
+    &lt;md5&gt;e6ac54ea0ab33dd6fd125a71fc34cf46&lt;/md5&gt;
</ins><span class="cx">   &lt;/timezone&gt;
</span><span class="cx">   &lt;timezone&gt;
</span><span class="cx">     &lt;tzid&gt;MET&lt;/tzid&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretwistedcaldavzoneinfoversiontxt"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/version.txt (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/version.txt        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/twistedcaldav/zoneinfo/version.txt        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -1 +1 @@
</span><del>-IANA Timezone Registry: 2013f
</del><span class="cx">\ No newline at end of file
</span><ins>+IANA Timezone Registry: 2013h
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretxdavbasepropertystorebasepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/base/propertystore/base.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/base/propertystore/base.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/base/propertystore/base.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -214,7 +214,13 @@
</span><span class="cx"> 
</span><span class="cx">     def __delitem__(self, key):
</span><span class="cx">         # Handle per-user behavior
</span><del>-        if self.isGlobalProperty(key):
</del><ins>+        if self.isShadowableProperty(key):
+            try:
+                self._delitem_uid(key, self._perUser)
+            except KeyError:
+                # It is OK for shadowable delete to fail
+                pass
+        elif self.isGlobalProperty(key):
</ins><span class="cx">             self._delitem_uid(key, self._defaultUser)
</span><span class="cx">         else:
</span><span class="cx">             self._delitem_uid(key, self._perUser)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboosharinginthestoretxdavbasepropertystoretestbasepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/base/propertystore/test/base.py (12015 => 12016)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/base/propertystore/test/base.py        2013-12-02 18:15:36 UTC (rev 12015)
+++ CalendarServer/branches/users/cdaboo/sharing-in-the-store/txdav/base/propertystore/test/base.py        2013-12-02 19:04:21 UTC (rev 12016)
</span><span class="lines">@@ -206,6 +206,41 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><ins>+    def test_peruserShadow_delete(self):
+        &quot;&quot;&quot;
+        Delete a shadowable property that has not been overridden by the sharee.
+        &quot;&quot;&quot;
+
+        name = propertyName(&quot;shadow&quot;)
+
+        self.propertyStore1.setSpecialProperties((name,), ())
+        self.propertyStore2.setSpecialProperties((name,), ())
+
+        value1 = propertyValue(&quot;Hello, World1!&quot;)
+
+        self.propertyStore1[name] = value1
+        yield self._changed(self.propertyStore1)
+        self.assertEquals(self.propertyStore1.get(name, None), value1)
+        self.assertEquals(self.propertyStore2.get(name, None), value1)
+        self.failUnless(name in self.propertyStore1)
+        self.failUnless(name in self.propertyStore2)
+
+        del self.propertyStore2[name]
+        yield self._changed(self.propertyStore2)
+        self.assertEquals(self.propertyStore1.get(name, None), value1)
+        self.assertEquals(self.propertyStore2.get(name, None), value1)
+        self.failUnless(name in self.propertyStore1)
+        self.failUnless(name in self.propertyStore2)
+
+        del self.propertyStore1[name]
+        yield self._changed(self.propertyStore1)
+        self.assertEquals(self.propertyStore1.get(name, None), None)
+        self.assertEquals(self.propertyStore2.get(name, None), None)
+        self.failIf(name in self.propertyStore1)
+        self.failIf(name in self.propertyStore2)
+
+
+    @inlineCallbacks
</ins><span class="cx">     def test_peruser_global(self):
</span><span class="cx"> 
</span><span class="cx">         name = propertyName(&quot;global&quot;)
</span></span></pre>
</div>
</div>

</body>
</html>