<!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"> "calendarserver_manage_postgres",
</span><span class="cx"> "calendarserver_manage_timezones",
</span><span class="cx"> "icalendar_split",
</span><ins>+ "twistd", "trial",
</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 "License");
+# 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 "AS IS" 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__ == "__main__":
+ if "PYTHONPATH" 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 "License");
+# 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 "AS IS" 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__ == "__main__":
+ if "PYTHONPATH" 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="/CalDAV/localhost/bar/",
</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="/CalDAV/localhost/bar/",
</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="/CalDAV/localhost/bar/",
</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="/CalDAV/localhost/baz/",
</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([("/CalDAV/localhost/bar/", PushPriority.high),
</span><span class="cx"> ("/CalDAV/localhost/baz/", 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="/CalDAV/localhost/bar/",
</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="/CalDAV/localhost/bar/",
</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="/CalDAV/localhost/bar/",
</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"> [("/CalDAV/localhost/bar/", 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"> """
</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"> "ical": True,
</span><span class="lines">@@ -555,7 +555,7 @@
</span><span class="cx"> """
</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"> "ical": True,
</span><span class="lines">@@ -627,7 +627,7 @@
</span><span class="cx"> """
</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"> "ical": False,
</span><span class="lines">@@ -667,7 +667,7 @@
</span><span class="cx"> """
</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"> "ical": False,
</span><span class="lines">@@ -1418,7 +1418,7 @@
</span><span class="cx"> sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
</span><span class="cx"> sync_token_old2 = (yield (yield self.calendarUnderTest(home=self.uuid2, name="calendar")).syncToken())
</span><span class="cx"> sync_token_old3 = (yield (yield self.calendarUnderTest(home=self.uuid3, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -1485,7 +1485,7 @@
</span><span class="cx"> sync_token_old1 = (yield (yield self.calendarUnderTest(home=self.uuid1, name="calendar")).syncToken())
</span><span class="cx"> sync_token_old2 = (yield (yield self.calendarUnderTest(home=self.uuid2, name="calendar")).syncToken())
</span><span class="cx"> sync_token_old3 = (yield (yield self.calendarUnderTest(home=self.uuid3, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": 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["fix"] = 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="calendar")).syncToken())
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": 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="calendar")).syncToken())
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": 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["fix"] = 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="calendar")).syncToken())
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": 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="calendar")).syncToken())
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": 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["fix"] = False
</span><span class="cx"> options["uuid"] = 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="calendar")).syncToken())
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -2592,7 +2592,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": False,
</span><span class="lines">@@ -2639,7 +2639,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": 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["fix"] = False
</span><span class="cx"> options["uuid"] = 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"> """
</span><span class="cx">
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": 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["fix"] = False
</span><span class="cx"> options["uuid"] = 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"> """
</span><span class="cx">
</span><span class="cx"> sync_token_oldl1 = (yield (yield self.calendarUnderTest(home=self.uuidl1, name="calendar")).syncToken())
</span><del>- self.commit()
</del><ins>+ yield self.commit()
</ins><span class="cx">
</span><span class="cx"> options = {
</span><span class="cx"> "ical": 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["fix"] = False
</span><span class="cx"> options["uuid"] = 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"> <key>EnableWebAdmin</key>
</span><span class="cx"> <true/>
</span><span class="cx">
</span><del>- <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
</del><ins>+ <!-- Support for Content-Encoding compression -->
</ins><span class="cx"> <key>ResponseCompression</key>
</span><del>- <false/>
</del><ins>+ <false/> <!-- Off for testing, as debugging is easier that way. -->
+
</ins><span class="cx">
</span><span class="cx"> <!-- The retry-after value (in seconds) to return with a 503 error. -->
</span><span class="cx"> <key>HTTPRetryAfter</key>
</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"> <key>EnableWebAdmin</key>
</span><span class="cx"> <true/>
</span><span class="cx">
</span><del>- <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
</del><ins>+ <!-- Support for Content-Encoding compression -->
</ins><span class="cx"> <key>ResponseCompression</key>
</span><span class="cx"> <false/>
</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 "ldap.h"
+# Assert that ldap.h is present with a version >= 20344
+# find_header "ldap.h" 20344 "LDAP_VENDOR_VERSION"
</ins><span class="cx"> find_header () {
</span><del>- local sysheader="$1"; shift;
- echo "#include <${sysheader}>" | cc -x c -c - -o /dev/null 2> /dev/null;
- return "$?";
-}
</del><ins>+ ARGS="$@";
+ ret=1; # default to a failed check, forcing a fetch of the depencency
+ i=0;
+ for a in $ARGS; do
+ [ $i -eq 0 ] && local sysheader="$1";
+ [ $i -eq 1 ] && local minver="$2";
+ [ $i -eq 2 ] && local def="$3";
+ i=$(($i+1));
+ done;
+ [ ! $sysheader ] && return 1;
+ # Check for presence of a header. We use the "-c" 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 "#include <${sysheader}>" | cc -x c -c - -o /dev/null 2> /dev/null;
+ return "$?";
+ # 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 <<DOC > ${prog}
+#include <${sysheader}>
+#include <stdio.h>
+#define STR(x) #x
+#define SHOW_DEFINE(x) printf("%s", STR(x))
+int main()
+{
+ if (${def})
+ {
+ SHOW_DEFINE(${def});
+ return 0;
+ };
+ return 1;
+};
+DOC
+ cc -x c -o ${aout} ${prog} &> /dev/null;
+ if [ $? -eq 0 ] && [ -e ${aout} ] ; then
+ found=$(${aout});
+ fi;
+ if [ $? -eq 0 ] && [ ! -z ${found} ] ; then
+ cmp_version $minver $found;
+ ret=$?;
+ else
+ ret=1; #cc exited nonzero or didn't emit a file
+ fi;
+ rm -f "${aout}";
+ rm -f "${prog}";
+ 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 "${force_setup}" || [ ! -d "${path}" ]; then
</span><span class="cx"> local ext="$(echo "${url}" | sed 's|^.*\.\([^.]*\)$|\1|')";
</span><span class="cx">
</span><ins>+ untar () { tar -xvf -; }
+ unzipstream () { tmp="$(mktemp -t ccsXXXXX)"; cat > "${tmp}"; unzip "${tmp}"; rm "${tmp}"; }
</ins><span class="cx"> case "${ext}" in
</span><del>- gz|tgz) decompress="gzip -d -c"; ;;
- bz2) decompress="bzip2 -d -c"; ;;
- tar) decompress="cat"; ;;
</del><ins>+ gz|tgz) decompress="gzip -d -c"; unpack="untar"; ;;
+ bz2) decompress="bzip2 -d -c"; unpack="untar"; ;;
+ tar) decompress="untar"; unpack="untar"; ;;
+ zip) decompress="cat"; unpack="unzipstream"; ;;
</ins><span class="cx"> *)
</span><span class="cx"> echo "Error in www_get of URL ${url}: Unknown extension ${ext}";
</span><span class="cx"> exit 1;
</span><span class="lines">@@ -228,7 +287,7 @@
</span><span class="cx"> if [ -n "${cache_deps}" ] && [ -n "${hash}" ]; then
</span><span class="cx"> mkdir -p "${cache_deps}";
</span><span class="cx">
</span><del>- local cache_basename="${name}-$(echo "${url}" | hash)-$(basename "${url}")";
</del><ins>+ local cache_basename="$(echo ${name} | tr '[ ]' '_')-$(echo "${url}" | hash)-$(basename "${url}")";
</ins><span class="cx"> local cache_file="${cache_deps}/${cache_basename}";
</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 "${path}";
</span><span class="cx"> cd "$(dirname "${path}")";
</span><del>- get | ${decompress} | tar -xvf -;
</del><ins>+ get | ${decompress} | ${unpack};
</ins><span class="cx"> apply_patches "${name}" "${path}";
</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 > /dev/null; then
</span><span class="cx"> using_system "memcached";
</span><span class="cx"> else
</span><del>- local le="libevent-2.0.17-stable";
- local mc="memcached-1.4.13";
- c_dependency -m "dad64aaaaff16b5fbec25160c06fee9a" \
</del><ins>+ local le="libevent-2.0.21-stable";
+ local mc="memcached-1.4.15";
+ c_dependency -m "b2405cc9ebf264aa47ff615d9de527a2" \
</ins><span class="cx"> "libevent" "${le}" \
</span><del>- "https://github.com/downloads/libevent/libevent/${le}.tar.gz";
- c_dependency -m "6d18c6d25da945442fcc1187b3b63b7f" \
</del><ins>+ "http://github.com/downloads/libevent/libevent/${le}.tar.gz";
+ c_dependency -m "36ea966f5a29655be1746bf4949f7f69" \
</ins><span class="cx"> "memcached" "${mc}" \
</span><span class="cx"> "http://memcached.googlecode.com/files/${mc}.tar.gz";
</span><span class="cx"> fi;
</span><span class="lines">@@ -683,8 +742,9 @@
</span><span class="cx"> if type -P postgres > /dev/null; then
</span><span class="cx"> using_system "Postgres";
</span><span class="cx"> else
</span><del>- local pgv="9.2.4";
- local pg="postgresql-${pgv}";
</del><ins>+ local v="9.3.1";
+ local n="postgresql";
+ local p="${n}-${v}";
</ins><span class="cx">
</span><span class="cx"> if type -P dtrace > /dev/null; then
</span><span class="cx"> local enable_dtrace="--enable-dtrace";
</span><span class="lines">@@ -692,19 +752,22 @@
</span><span class="cx"> local enable_dtrace="";
</span><span class="cx"> fi;
</span><span class="cx">
</span><del>- c_dependency -m "52df0a9e288f02d7e6e0af89ed4dcfc6" \
- "PostgreSQL" "${pg}" \
- "ftp://ftp5.us.postgresql.org/pub/PostgreSQL/source/v${pgv}/${pg}.tar.gz" \
</del><ins>+ c_dependency -m "c003d871f712d4d3895956b028a96e74" \
+ "PostgreSQL" "${p}" \
+ "http://ftp.postgresql.org/pub/source/v${v}/${p}.tar.bz2" \
</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 "OpenLDAP";
</span><span class="cx"> else
</span><del>- c_dependency -m "ec63f9c2add59f323a0459128846905b" \
- "OpenLDAP" "openldap-2.4.25" \
- "http://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-2.4.25.tgz" \
</del><ins>+ local v="2.4.38";
+ local n="openldap";
+ local p="${n}-${v}";
+ c_dependency -m "39831848c731bcaef235a04e0d14412f" \
+ "OpenLDAP" "${p}" \
+ "http://www.openldap.org/software/download/OpenLDAP/${n}-release/${p}.tgz" \
</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="superb-sea2.dl.sourceforge.net";
</span><del>- local st="setuptools-0.6c11";
</del><ins>+ local st="setuptools-1.4";
</ins><span class="cx"> local pypi="http://pypi.python.org/packages/source";
</span><span class="cx">
</span><del>- py_dependency -m "7df2a529a074f613b509fb44feefe74e" \
</del><ins>+ py_dependency -v 1 -m "5710464bc5a61d75f5087f15ce63cfe0" \
</ins><span class="cx"> "setuptools" "setuptools" "${st}" \
</span><span class="cx"> "$pypi/s/setuptools/${st}.tar.gz";
</span><span class="cx">
</span><del>- local v="4.0.3";
</del><ins>+ local v="4.0.5";
</ins><span class="cx"> local n="zope.interface";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v 4 -m "1ddd308f2c83703accd1696158c300eb" \
</del><ins>+ py_dependency -v 4 -m "caf26025ae1b02da124a58340e423dfe" \
</ins><span class="cx"> "Zope Interface" "${n}" "${p}" \
</span><del>- "http://pypi.python.org/packages/source/z/${n}/${p}.tar.gz";
</del><ins>+ "http://pypi.python.org/packages/source/z/${n}/${p}.zip";
</ins><span class="cx">
</span><del>- local v="0.10";
</del><ins>+ local v="0.12";
</ins><span class="cx"> local n="pyOpenSSL";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v 0.9 -m "34db8056ec53ce80c7f5fc58bee9f093" \
</del><ins>+ py_dependency -v 0.12 -m "60a7bbb6160950823eddcbba2cbcb0d6" \
</ins><span class="cx"> "${n}" "OpenSSL" "${p}" \
</span><span class="cx"> "http://pypi.python.org/packages/source/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><span class="lines">@@ -754,18 +817,18 @@
</span><span class="cx"> "${svn_uri_base}/${n}/trunk";
</span><span class="cx"> fi;
</span><span class="cx">
</span><del>- local v="0.6.1";
</del><ins>+ local v="0.6.4";
</ins><span class="cx"> local n="xattr";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v 0.5 -r 1038 \
- "${n}" "${n}" "${n}" \
- "http://svn.red-bean.com/bob/${n}/releases/${p}/";
</del><ins>+ py_dependency -v 0.6 -m "1bef31afb7038800f8d5cfa2f4562b37" \
+ "${n}" "${n}" "${p}" \
+ "${pypi}/x/${n}/${n}-${v}.tar.gz";
</ins><span class="cx">
</span><span class="cx"> if [ -n "${ORACLE_HOME:-}" ]; then
</span><del>- local v="5.1";
</del><ins>+ local v="5.1.2";
</ins><span class="cx"> local n="cx_Oracle";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "${v}" -m "d2697493a40c9d46c9b7c1c210b61671" \
</del><ins>+ py_dependency -v "${v}" -m "462f309e00f7bff7100e2077fc43172c" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "http://${sf}/project/cx-oracle/${v}/${p}.tar.gz";
</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="12.3.0";
</del><ins>+ local v="13.2.0";
</ins><span class="cx"> local n="Twisted";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v 12.2 -m "6e289825f3bf5591cfd670874cc0862d" \
</del><ins>+ py_dependency -v 13.2 -m "83fe6c0c911cc1602dbffb036be0ba79" \
</ins><span class="cx"> "${n}" "twisted" "${p}" \
</span><span class="cx"> "${pypi}/T/${n}/${p}.tar.bz2";
</span><span class="cx">
</span><span class="lines">@@ -793,22 +856,22 @@
</span><span class="cx"> "${n}" "dateutil" "${p}" \
</span><span class="cx"> "http://www.labix.org/download/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="0.6.1";
</del><ins>+ local v="1.2.0";
</ins><span class="cx"> local n="psutil";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -m "3cfcbfb8525f6e4c70110e44a85e907e" \
</del><ins>+ py_dependency -m "f8ae906249e65db21f17d873ae07e584" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><del>- "http://${n}.googlecode.com/files/${p}.tar.gz";
</del><ins>+ "${pypi}/p/${n}/${p}.tar.gz";
</ins><span class="cx">
</span><del>- local v="2.3.13";
</del><ins>+ local v="2.4.13";
</ins><span class="cx"> local n="python-ldap";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "${v}" -m "895223d32fa10bbc29aa349bfad59175" \
</del><ins>+ py_dependency -v "${v}" -m "74b7b50267761540451eade44b2049ee" \
</ins><span class="cx"> "Python-LDAP" "ldap" "${p}" \
</span><span class="cx"> "${pypi}/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><span class="cx"> # XXX actually PyCalendar should be imported in-place.
</span><del>- py_dependency -fe -i "src" -r 11914 \
</del><ins>+ py_dependency -fe -i "src" -r 11947 \
</ins><span class="cx"> "PyCalendar" "pycalendar" "pycalendar" \
</span><span class="cx"> "${svn_uri_base}/PyCalendar/trunk";
</span><span class="cx">
</span><span class="lines">@@ -840,44 +903,45 @@
</span><span class="cx"> "${svn_uri_base}/CalDAVClientLibrary/trunk";
</span><span class="cx">
</span><span class="cx"> # Can't add "-v 2011g" to args because the version check expects numbers.
</span><ins>+ local v="2013.8";
</ins><span class="cx"> local n="pytz";
</span><del>- local p="${n}-2011n";
- py_dependency -m "75ffdc113a4bcca8096ab953df746391" \
</del><ins>+ local p="${n}-${v}";
+ py_dependency -m "37750ca749ed3a52523b9682b0b7e381" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="2.5";
</del><ins>+ local v="2.6.1";
</ins><span class="cx"> local n="pycrypto";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "${v}" -m "783e45d4a1a309e03ab378b00f97b291" \
</del><ins>+ py_dependency -v "${v}" -m "55a61a054aa66812daf5161a0d5d7eda" \
</ins><span class="cx"> "PyCrypto" "${n}" "${p}" \
</span><span class="cx"> "http://ftp.dlitz.net/pub/dlitz/crypto/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="0.1.2";
</del><ins>+ local v="0.1.7";
</ins><span class="cx"> local n="pyasn1";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "${v}" -m "a7c67f5880a16a347a4d3ce445862a47" \
</del><ins>+ py_dependency -v "${v}" -m "2cbd80fcd4c7b1c82180d3d76fee18c8" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="1.1.6";
</del><ins>+ local v="1.1.8";
</ins><span class="cx"> local n="setproctitle";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "1.0" -m "1e42e43b440214b971f4b33c21eac369" \
</del><ins>+ py_dependency -v "1.0" -m "728f4c8c6031bbe56083a48594027edd" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/s/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="0.6";
</del><ins>+ local v="0.8";
</ins><span class="cx"> local n="cffi";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "0.6" -m "5be33b1ab0247a984d42b27344519337" \
</del><ins>+ py_dependency -v "0.6" -m "e61deb0515311bb42d5d58b9403bc923" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/c/${n}/${p}.tar.gz";
</span><span class="cx">
</span><del>- local v="2.09.1";
</del><ins>+ local v="2.10";
</ins><span class="cx"> local n="pycparser";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -v "0.6" -m "74aa075fc28b7c24a4426574d1ac91e0" \
</del><ins>+ py_dependency -v "0.6" -m "d87aed98c8a9f386aa56d365fe4d515f" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><span class="cx"> "${pypi}/p/${n}/${p}.tar.gz";
</span><span class="cx">
</span><span class="lines">@@ -889,21 +953,21 @@
</span><span class="cx"> local p="${n}-${v}";
</span><span class="cx"> py_dependency -o -m "36407974bd5da2af00bf90ca27feeb44" \
</span><span class="cx"> "Epydoc" "${n}" "${p}" \
</span><del>- "https://pypi.python.org/packages/source/e/${n}/${p}.tar.gz";
</del><ins>+ "${pypi}/e/${n}/${p}.tar.gz";
</ins><span class="cx">
</span><span class="cx"> local v="0.10.0";
</span><span class="cx"> local n="Nevow";
</span><span class="cx"> local p="${n}-${v}";
</span><span class="cx"> py_dependency -o -m "66dda2ad88f42dea05911add15f4d1b2" \
</span><span class="cx"> "${n}" "${n}" "${p}" \
</span><del>- "https://pypi.python.org/packages/source/N/${n}/${p}.tar.gz";
</del><ins>+ "${pypi}/N/${n}/${p}.tar.gz";
</ins><span class="cx">
</span><del>- local v="0.4";
</del><ins>+ local v="0.5b1";
</ins><span class="cx"> local n="pydoctor";
</span><span class="cx"> local p="${n}-${v}";
</span><del>- py_dependency -o -m "b7564e12b5d35d4cb529a2c220b25d3a" \
</del><ins>+ py_dependency -o -m "c4fb33672f37624116cc7a0606f74f28" \
</ins><span class="cx"> "${n}" "${n}" "${p}" \
</span><del>- "https://pypi.python.org/packages/source/p/${n}/${p}.tar.gz";
</del><ins>+ "{$pypi}/p/${n}/${p}.tar.gz";
</ins><span class="cx">
</span><span class="cx"> if "${do_setup}"; then
</span><span class="cx"> cd "${caldav}";
</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 = "5.2"
</del><ins>+ base_version = "6.0"
</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="$(cd "$(dirname "$0")" && pwd -L)";
</span><span class="cx">
</span><del>-. "${wd}/support/build.sh";
</del><ins>+#. "${wd}/support/build.sh";
</ins><span class="cx">
</span><span class="cx"> do_setup="false";
</span><span class="cx"> do_get="false";
</span><span class="lines">@@ -74,9 +74,6 @@
</span><span class="cx">
</span><span class="cx"> export PYTHONPATH="${wd}:${PYTHONPATH:-}";
</span><span class="cx">
</span><del>-dependencies;
-trial="$(type -p trial)";
-
</del><span class="cx"> if [ $# -gt 0 ]; then
</span><span class="cx"> test_modules="$@";
</span><span class="cx"> flaky=true;
</span><span class="lines">@@ -88,7 +85,7 @@
</span><span class="cx"> find "${wd}" -name \*.pyc -print0 | xargs -0 rm;
</span><span class="cx">
</span><span class="cx"> mkdir -p "${wd}/data";
</span><del>-cd "${wd}" && "${python}" "${trial}" --temp-directory="${wd}/data/trial" --rterrors ${reactor} ${random} ${until_fail} ${no_colour} ${coverage} ${numjobs} ${test_modules};
</del><ins>+cd "${wd}" && "${wd}/bin/trial" --temp-directory="${wd}/data/trial" --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 "";
</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"> """
</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"> """
</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):
+ """
+ Removes a previously added socket from the pool of sockets being used
+ for transmitting file descriptors to child processes.
+ """
+ for a in self._subprocessSockets:
+ if a.inSocket == skt:
+ self._subprocessSockets.remove(a)
+ break
+ else:
+ raise ValueError("Unknown socket: {0}".format(skt))
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class InheritedPort(FileDescriptor, object):
</span><span class="cx"> """
</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 "License");
+# 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 "AS IS" 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.
+##
+
+"""
+Echo protocol.
+"""
+
+__all__ = ["EchoProtocol"]
+
+from twisted.internet.protocol import Protocol
+
+
+class EchoProtocol(Protocol):
+ """
+ Say what you hear.
+ """
+
+ def dataReceived(self, data):
+ """
+ As soon as any data is received, write it back.
+ """
+ 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"> """
</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"> """
</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::
+
+ {
+ <FieldName1>: {
+ <value1a>: set([<record1a1>, ...]),
+ ...
+ },
+ ...
+ }
+
+ Here is an example index for a service with a three user records and one
+ group record::
+
+ {
+ <FieldName=uid>: {
+ u'__calendar-dev__': set([
+ <DirectoryRecord (group)calendar-dev>
+ ]),
+ u'__dre__': set([
+ <DirectoryRecord (user)dre>
+ ]),
+ u'__sagen__': set([
+ <DirectoryRecord (user)sagen>
+ ]),
+ u'__wsanchez__': set([
+ <DirectoryRecord (user)wsanchez>
+ ])
+ },
+ <FieldName=recordType>: {
+ <RecordType=group>: set([
+ <DirectoryRecord (group)calendar-dev>,
+ ]),
+ <RecordType=user>: set([
+ <DirectoryRecord (user)sagen>,
+ <DirectoryRecord (user)wsanchez>
+ ])
+ },
+ <FieldName=shortNames>: {
+ u'calendar-dev': set([<DirectoryRecord (group)calendar-dev>]),
+ u'dre': set([<DirectoryRecord (user)dre>]),
+ u'sagen': set([<DirectoryRecord (user)sagen>]),
+ u'wilfredo_sanchez': set([<DirectoryRecord (user)wsanchez>]),
+ u'wsanchez': set([<DirectoryRecord (user)wsanchez>])
+ },
+ <FieldName=emailAddresses>: {
+ 'dev@bitbucket.calendarserver.org': set([
+ <DirectoryRecord (group)calendar-dev>
+ ]),
+ 'dre@bitbucket.calendarserver.org': set([
+ <DirectoryRecord (user)dre>
+ ]),
+ 'sagen@bitbucket.calendarserver.org': set([
+ <DirectoryRecord (user)sagen>
+ ]),
+ 'shared@example.com': set([
+ <DirectoryRecord (user)sagen>,
+ <DirectoryRecord (user)dre>
+ ]),
+ 'wsanchez@bitbucket.calendarserver.org': set([
+ <DirectoryRecord (user)wsanchez>
+ ]),
+ 'wsanchez@devnull.twistedmatrix.com': set([
+ <DirectoryRecord (user)wsanchez>
+ ])
+ },
+ <FieldName=memberUIDs>: {
+ u'__sagen__': set([<DirectoryRecord (group)calendar-dev>]),
+ u'__wsanchez__': set([<DirectoryRecord (group)calendar-dev>])
+ }
+ }
+
+ 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"> """
</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"> """
</span><del>- Call L{loadRecords}C{()} and return the index.
</del><ins>+ Call L{loadRecords} and return the index.
</ins><span class="cx"> """
</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"> """
</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"> """
</span><del>- self._index = value
</del><ins>+ raise NotImplementedError("Subclasses must implement loadRecords().")
</ins><span class="cx">
</span><span class="cx">
</span><del>- def loadRecords(self):
</del><ins>+ def indexRecords(self, records):
</ins><span class="cx"> """
</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"> """
</span><del>- raise NotImplementedError("Subclasses must implement loadRecords().")
</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"> """
</span><span class="cx"> Flush the index.
</span><span class="cx"> """
</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"> """
</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(
+ "indexedRecordsFromMatchExpression() was passed an "
+ "expression with an unindexed field: {0!r}"
+ .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>- "Unknown match type: {0}".format(describe(matchType))
</del><ins>+ "Unknown match type: {0!r}".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>- "Unknown match type: {0}".format(describe(matchType))
</del><ins>+ "Unknown match type: {0!r}".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>+ """
+ This implementation can handle L{MatchExpression} expressions; other
+ expressions are passed up to the superclass.
+ """
</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), "<TestService u'xyzzy'>")
</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):
+ """
+ Stub directory service with some built-in records and an implementation
+ of C{recordsFromNonCompoundExpression}.
+ """
+
+ 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):
+ """
+ This implementation handles three expressions:
+
+ The expression C{u"None"} will match no records.
+
+ The expressions C{u"twistedmatrix.com"} and C{u"calendarserver.org"}
+ will match records that have an email address ending with the
+ given expression.
+ """
+ self.seenExpressions.append(expression)
+
+ if expression == u"None":
+ return succeed([])
+
+ if expression in (u"twistedmatrix.com", u"calendarserver.org"):
+ 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"> """
</span><span class="cx"> MixIn that sets up a service appropriate for testing.
</span><span class="cx"> """
</span><ins>+
</ins><span class="cx"> realmName = u"xyzzy"
</span><span class="cx">
</span><span class="cx">
</span><del>- def service(self):
- if not hasattr(self, "_service"):
- 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"> """
</span><del>- C{repr} returns the expected string.
</del><ins>+ L{DirectoryService.repr} returns the expected string.
</ins><span class="cx"> """
</span><span class="cx"> service = self.service()
</span><del>- self.assertEquals(repr(service), "<DirectoryService u'xyzzy'>")
</del><ins>+ self.assertEquals(
+ repr(service),
+ "<{0} u'xyzzy'>".format(self.serviceClass.__name__)
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_recordTypes(self):
</span><span class="cx"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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
+):
+ """
+ Tests for L{DirectoryService.recordsFromExpression}.
+ """
+ serviceClass = StubDirectoryService
+ directoryRecordClass = DirectoryRecord
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_recordsFromExpression_single(self):
</span><span class="cx"> """
</span><del>- C{recordsFromExpression} handles a single expression
</del><ins>+ L{DirectoryService.recordsFromExpression} handles a single expression.
</ins><span class="cx"> """
</span><del>- service = StubDirectoryService()
</del><ins>+ service = self.service()
</ins><span class="cx">
</span><span class="cx"> result = yield service.recordsFromExpression("twistedmatrix.com")
</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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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"> """
</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
+):
+ """
+ Tests for L{DirectoryService} convenience methods.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
</ins><span class="cx"> def test_recordWithUID(self):
</span><span class="cx"> """
</span><del>- C{recordWithUID} fails with L{QueryNotSupportedError}.
</del><ins>+ L{DirectoryService.recordWithUID} fails with L{QueryNotSupportedError}.
</ins><span class="cx"> """
</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"> """
</span><del>- C{recordWithGUID} fails with L{QueryNotSupportedError}.
</del><ins>+ L{DirectoryService.recordWithGUID} fails with
+ L{QueryNotSupportedError}.
</ins><span class="cx"> """
</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"> """
</span><del>- C{recordsWithRecordType} fails with L{QueryNotSupportedError}.
</del><ins>+ L{DirectoryService.recordsWithRecordType} fails with
+ L{QueryNotSupportedError}.
</ins><span class="cx"> """
</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"> """
</span><del>- C{recordWithShortName} fails with L{QueryNotSupportedError}.
</del><ins>+ L{DirectoryService.recordWithShortName} fails with
+ L{QueryNotSupportedError}.
</ins><span class="cx"> """
</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"> """
</span><del>- C{recordsWithEmailAddress} fails with L{QueryNotSupportedError}.
</del><ins>+ L{DirectoryService.recordsWithEmailAddress} fails with
+ L{QueryNotSupportedError}.
</ins><span class="cx"> """
</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"> """
</span><del>- Immutable directory record tests.
</del><ins>+ Tests for immutable directory services.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> def test_updateRecordsNotAllowed(self):
</span><span class="lines">@@ -409,7 +488,7 @@
</span><span class="cx"> """
</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"__plugh__",
</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>+ """
+ Tests for immutable L{DirectoryService}.
+ """
+ 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>+ """
+ Tests for directory records.
+ """
+
</ins><span class="cx"> fields_wsanchez = {
</span><span class="cx"> FieldName.uid: u"UID:wsanchez",
</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"> """
</span><del>- C{repr} returns the expected string.
</del><ins>+ L{DirectoryRecord.repr} returns the expected string.
</ins><span class="cx"> """
</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"> """
</span><del>- C{description} returns the expected string.
</del><ins>+ L{DirectoryRecord.description} returns the expected string.
</ins><span class="cx"> """
</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 = "Intermittent order issues"
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_members_group(self):
</span><span class="cx"> """
</span><del>- Group members.
</del><ins>+ Group members for group records.
</ins><span class="cx"> """
</span><del>- raise SkipTest("Subclasses should implement this test.")
</del><ins>+ raise NotImplementedError("Subclasses should implement this test.")
</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"> """
</span><del>- Non-groups have no members.
</del><ins>+ Group members for non-group records. Non-groups have no members.
</ins><span class="cx"> """
</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"> """
</span><span class="cx"> Group memberships.
</span><span class="cx"> """
</span><del>- raise SkipTest("Subclasses should implement this test.")
</del><ins>+ raise NotImplementedError("Subclasses should implement this test.")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class DirectoryRecordTest(unittest.TestCase, BaseDirectoryRecordTest):
</span><ins>+ """
+ Tests for L{DirectoryRecord}.
+ """
+ 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"> """
</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"> """
</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"Stub")
</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"> """
</span><span class="cx"> Add a known set of records to this service.
</span><span class="cx"> """
</span><del>- self._addUser(
</del><ins>+ self.addUser(
</ins><span class="cx"> shortNames=[u"wsanchez", u"wilfredo_sanchez"],
</span><del>- fullNames=[u"Wilfredo S\xe1nchez Vega"],
</del><ins>+ fullNames=[
+ u"Wilfredo S\xe1nchez Vega",
+ u"Wilfredo Sanchez Vega",
+ u"Wilfredo Sanchez",
+ ],
</ins><span class="cx"> emailAddresses=[
</span><span class="cx"> u"wsanchez@bitbucket.calendarserver.org",
</span><span class="cx"> u"wsanchez@devnull.twistedmatrix.com",
</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"glyph"],
</span><span class="cx"> fullNames=[u"Glyph Lefkowitz"],
</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"sagen"],
</span><span class="cx"> fullNames=[u"Morgen Sagen"],
</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"cdaboo"],
</span><span class="cx"> fullNames=[u"Cyrus Daboo"],
</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"dre"],
</span><span class="cx"> fullNames=[u"Andre LaBranche"],
</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"exarkun"],
</span><span class="cx"> fullNames=[u"Jean-Paul Calderone"],
</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"dreid"],
</span><span class="cx"> fullNames=[u"David Reid"],
</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"joe"],
</span><span class="cx"> fullNames=[u"Joe Schmoe"],
</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"alyssa"],
</span><span class="cx"> fullNames=[u"Alyssa P. Hacker"],
</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"> """
</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"> """
</span><del>- self.records.append(DirectoryRecord(self, {
- self.fieldName.recordType: self.recordType.user,
- self.fieldName.uid: u"__{0}__".format(shortNames[0]),
- self.fieldName.shortNames: shortNames,
- self.fieldName.fullNames: fullNames,
- self.fieldName.password: u"".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"__{0}__".format(shortNames[0]),
+ fieldName.shortNames: shortNames,
+ fieldName.fullNames: fullNames,
+ fieldName.password: u"".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"None":
- return succeed([])
-
- if expression in (u"twistedmatrix.com", u"calendarserver.org"):
- 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"> """
</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 "License");
+# 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 "AS IS" 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.
+##
+
+"""
+Indexed directory service base implementation tests.
+"""
+
+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):
+ """
+ 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}
+ """
+ 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):
+ """
+ Tests for indexed directory services.
+ """
+
+ 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):
+ """
+ L{DirectoryService.indexRecords} ensures all record data is in the
+ index.
+ """
+ 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):
+ """
+ L{DirectoryService.indexRecords} does not have extra data in the index.
+ """
+ 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):
+ """
+ C{flush} empties the index.
+ """
+ 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):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with a startsWith
+ expression.
+ """
+ return self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"w", (u"__wsanchez__",)), # Duplicates
+ (u"dr", (u"__dre__", u"__dreid__")), # Multiple
+ (u"sage", (u"__sagen__",)), # Single
+ ),
+ MatchType.startsWith
+ )
+
+
+ def test_indexedRecordsFromMatchExpression_contains(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with a contains
+ expression.
+ """
+ return self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"sanch", (u"__wsanchez__",)), # Duplicates
+ (u"dr", (u"__dre__", u"__dreid__")), # Multiple
+ (u"agen", (u"__sagen__",)), # Single
+ ),
+ MatchType.contains
+ )
+
+
+ def test_indexedRecordsFromMatchExpression_equals(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with an equals
+ expression.
+ """
+ return self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"wsanchez", (u"__wsanchez__",)), # MultiValue
+ (u"dre", (u"__dre__",)), # Single value
+ ),
+ MatchType.equals
+ )
+
+
+ def test_indexedRecordsFromMatchExpression_notIndexed(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with an
+ unindexed field name.
+ """
+ result = self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"zehcnasw", (u"__wsanchez__",)),
+ ),
+ MatchType.equals,
+ fieldName=BaseFieldName.password
+ )
+ self.assertFailure(result, TypeError)
+
+
+ def test_indexedRecordsFromMatchExpression_notMatchExpression(self):
+ """
+ L{DirectoryService.indexedRecordsFromMatchExpression} with a
+ non-match expression.
+ """
+ result = self._test_indexedRecordsFromMatchExpression(
+ (
+ (u"zehcnasw", (u"__wsanchez__",)),
+ ),
+ "Not a match type we know about"
+ )
+ 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):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with a
+ startsWith expression.
+ """
+ return self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"Wilfredo", (u"__wsanchez__",)), # Duplicates
+ (u"A", (u"__alyssa__", u"__dre__")), # Multiple
+ (u"Andre", (u"__dre__",)), # Single
+ ),
+ MatchType.startsWith
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_contains(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with a contains
+ expression.
+ """
+ return self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"Sanchez", (u"__wsanchez__",)), # Duplicates
+ (u"A", (u"__alyssa__", u"__dre__")), # Multiple
+ (u"LaBra", (u"__dre__",)), # Single
+ ),
+ MatchType.contains
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_equals(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with an equals
+ expression.
+ """
+ return self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"Wilfredo Sanchez", (u"__wsanchez__",)), # MultiValue
+ (u"Andre LaBranche", (u"__dre__",)), # Single value
+ ),
+ MatchType.equals
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_indexed(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with an
+ indexed field name.
+ """
+ self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"wsanchez", (u"__wsanchez__",)),
+ ),
+ MatchType.equals,
+ fieldName=BaseFieldName.shortNames
+ )
+
+
+ def test_unIndexedRecordsFromMatchExpression_notMatchExpression(self):
+ """
+ L{DirectoryService.unIndexedRecordsFromMatchExpression} with a
+ non-match expression.
+ """
+ result = self._test_unIndexedRecordsFromMatchExpression(
+ (
+ (u"zehcnasw", (u"__wsanchez__",)),
+ ),
+ "Not a match type we know about"
+ )
+ 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):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with a
+ L{MatchExpression} for an indexed field calls
+ L{DirectoryRecord.indexedRecordsFromMatchExpression}.
+ """
+ service = yield self._test_recordsFromNonCompoundExpression(
+ MatchExpression(BaseFieldName.shortNames, u"...")
+ )
+ self.assertTrue(getattr(service, "_calledIndexed", False))
+ self.assertFalse(getattr(service, "_calledUnindexed", False))
+
+
+ @inlineCallbacks
+ def test_recordsFromNonCompoundExpression_match_unindexed(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with a
+ L{MatchExpression} for an unindexed field calls
+ L{DirectoryRecord.unIndexedRecordsFromMatchExpression}.
+ """
+ service = yield self._test_recordsFromNonCompoundExpression(
+ MatchExpression(BaseFieldName.password, u"...")
+ )
+ self.assertFalse(getattr(service, "_calledIndexed", False))
+ self.assertTrue(getattr(service, "_calledUnindexed", False))
+
+
+ def test_recordsFromNonCompoundExpression_unknown(self):
+ """
+ L{DirectoryService.recordsFromNonCompoundExpression} with a
+ an unknown expression calls superclass, which will result in a
+ L{QueryNotSupportedError}.
+ """
+ result = self._test_recordsFromNonCompoundExpression(object())
+ self.assertFailure(result, QueryNotSupportedError)
+
+
+
+class DirectoryServiceTest(unittest.TestCase, BaseDirectoryServiceTest):
+ """
+ Tests for L{DirectoryService}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
+ def test_init_noIndex(self):
+ """
+ Index starts as C{None}.
+ """
+ service = self.service()
+ self.assertTrue(emptyIndex(service._index))
+
+
+ def test_index_get(self):
+ """
+ Getting the C{index} property calls C{loadRecords}.
+ """
+ class TestService(DirectoryService):
+ loaded = False
+
+ def loadRecords(self):
+ self.loaded = True
+
+ service = TestService(u"")
+ service.index
+ self.assertTrue(service.loaded)
+
+
+ def test_loadRecords(self):
+ """
+ L{DirectoryService.loadRecords} raises C{NotImplementedError}.
+ """
+ service = self.service()
+ self.assertRaises(NotImplementedError, service.loadRecords)
+
+
+ def _noop(self):
+ """
+ Does nothing.
+ """
+
+
+ test_recordWithUID = _noop
+ test_recordWithGUID = _noop
+ test_recordsWithRecordType = _noop
+ test_recordWithShortName = _noop
+ test_recordsWithEmailAddress = _noop
+
+
+
+class BaseDirectoryServiceImmutableTest(
+ test_directory.BaseDirectoryServiceImmutableTest
+):
+ """
+ Tests for immutable indexed directory services.
+ """
+
+
+
+class DirectoryServiceImmutableTest(
+ unittest.TestCase, BaseDirectoryServiceImmutableTest
+):
+ """
+ Tests for immutable L{DirectoryService}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
+
+class BaseDirectoryRecordTest(test_directory.BaseDirectoryRecordTest):
+ """
+ Tests for indexed directory records.
+ """
+
+
+
+class DirectoryRecordTest(unittest.TestCase, BaseDirectoryRecordTest):
+ """
+ Tests for L{DirectoryRecord}.
+ """
+ serviceClass = DirectoryService
+ directoryRecordClass = DirectoryRecord
+
+
+ def _noop(self):
+ """
+ Does nothing.
+ """
+
+
+ test_members_group = _noop
+ test_memberships = _noop
+
+
+
+def emptyIndex(index):
+ """
+ Determine whether an index is empty.
+
+ @param index: An index.
+ @type index: L{dict}
+
+ @return: true if C{index} is empty, otherwise false.
+ """
+ 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), "<TestService (not loaded)>")
- service.loadRecords()
- self.assertEquals(repr(service), "<TestService u'xyzzy'>")
-
-
</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), "<TestService (not loaded)>")
+ service.loadRecords()
+ self.assertEquals(repr(service), "<TestService u'xyzzy'>")
+
+
+
+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("Incorrect root element"), e)
</span><span class="cx"> else:
</span><del>- raise AssertionError
</del><ins>+ raise AssertionError("Expected ParseError")
</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("No realm name"), e)
</span><span class="cx"> else:
</span><del>- raise AssertionError
</del><ins>+ raise AssertionError("Expected ParseError")
</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"__plugh__",
</del><ins>+ service.fieldName.uid: u"__plugh__",
</ins><span class="cx"> service.fieldName.recordType: service.recordType.user,
</span><span class="cx"> service.fieldName.shortNames: (u"plugh",),
</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"__plugh__",
</del><ins>+ service.fieldName.uid: u"__plugh__",
</ins><span class="cx"> service.fieldName.recordType: service.recordType.user,
</span><span class="cx"> service.fieldName.shortNames: (u"plugh",),
</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"__wsanchez__"))
</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"__wsanchez__"))
</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(
+ "Unable to instantiate XML service {0}: {1}"
+ .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("No realm name.")
</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 "License");
+# 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 "AS IS" 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(
+ "master",
+ "Master process application container",
+ MasterOptions,
+ "twext.application.masterchild.MasterServiceMaker"
+)
+
+
+
+childServiceMaker = ServiceMaker(
+ "child",
+ "Child process application container",
+ ChildOptions,
+ "twext.application.masterchild.ChildServiceMaker"
+)
</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"> """
</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"> """
</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):
+ """
+ Returns a multi-value C{list} for the given parameter.
+ """
+ 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>- "FailIfUpgradeNeeded" : True, # Set to True to prevent the server or utility tools
</del><ins>+ "FailIfUpgradeNeeded" : 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"> "StopAfterUpgradeTriggerFile" : "stop_after_upgrade", # 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>- "ResponseCompression": True,
</del><ins>+ # Defaults off, because it weakens TLS (CRIME attack).
+ "ResponseCompression": 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"> "HTTPRetryAfter": 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"> <dict>
</span><span class="cx">
</span><span class="cx"> <key>ResponseCompression</key>
</span><del>- <false/>
</del><ins>+ <true/>
</ins><span class="cx">
</span><span class="cx"> <key>HTTPPort</key>
</span><span class="cx"> <integer>8008</integer>
</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("\r", ""))
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def test_parameter_multi_values(self):
+ caldata = """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="urn:uuid:group01","urn:uuid:group02";PARTSTAT=NEEDS-ACTION:mailto:user02@example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01@example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ caldata2 = """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="urn:uuid:group01","urn:uuid:group02","urn:uuid:group03";PARTSTAT=NEEDS-ACTION:mailto:user02@example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01@example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ caldata3 = """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="urn:uuid:group01";PARTSTAT=NEEDS-ACTION:mailto:user02@example.com
+DTSTAMP:20080601T120000Z
+ORGANIZER:mailto:user01@example.com
+END:VEVENT
+END:VCALENDAR
+"""
+
+ component = Component.fromString(caldata)
+ attendee = component.masterComponent().getAttendeeProperty(["mailto:user02@example.com", ])
+ self.assertTrue(attendee is not None)
+
+ # Single value retrieved as multi-value
+ partstat = attendee.parameterValues("PARTSTAT")
+ self.assertEqual(partstat, ["NEEDS-ACTION"])
+
+ # Multi-value retrieved as single-value
+ member = attendee.parameterValue("MEMBER")
+ self.assertEqual(member, "urn:uuid:group01")
+
+ # Multi-value retrieved as multi-value
+ members = attendee.parameterValues("MEMBER")
+ self.assertEqual(members, ["urn:uuid:group01", "urn:uuid:group02"])
+
+ # Multi-value add a new value
+ members = attendee.parameterValues("MEMBER")
+ members.append("urn:uuid:group03")
+ attendee.setParameter("MEMBER", members)
+ members = attendee.parameterValues("MEMBER")
+ self.assertEqual(members, ["urn:uuid:group01", "urn:uuid:group02", "urn:uuid:group03"])
+ self.assertEqual(normalize_iCalStr(str(component)), normalize_iCalStr(caldata2))
+
+ # Multi-value back to one
+ members = attendee.parameterValues("MEMBER")
+ del members[1:]
+ attendee.setParameter("MEMBER", members)
+ members = attendee.parameterValues("MEMBER")
+ self.assertEqual(members, ["urn:uuid:group01"])
+ 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 = """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"> <!DOCTYPE timezones SYSTEM "timezones.dtd">
</span><span class="cx">
</span><span class="cx"> <timezones>
</span><del>- <dtstamp>2013-10-01T01:19:11Z</dtstamp>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
</ins><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Abidjan</tzid>
</span><span class="cx"> <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</span><span class="lines">@@ -78,8 +78,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Casablanca</tzid>
</span><del>- <dtstamp>2013-07-11T02:11:45Z</dtstamp>
- <md5>b4e345b053c4699911078dcd16854bab</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>4f58dbcb4f7e6dfa7af3d710aeff97b4</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Ceuta</tzid>
</span><span class="lines">@@ -113,8 +113,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/El_Aaiun</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>494500808e8542fd83e4706654c43545</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>7933d45461e9f988ebfc9d062ce894e4</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Freetown</tzid>
</span><span class="lines">@@ -264,9 +264,9 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Tripoli</tzid>
</span><del>- <dtstamp>2013-01-14T15:32:16Z</dtstamp>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
</ins><span class="cx"> <alias>Libya</alias>
</span><del>- <md5>f59e5f16eec995c112b8b27580922fdd</md5>
</del><ins>+ <md5>6e8040bfd898654905bfd0a49c64e365</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Africa/Tunis</tzid>
</span><span class="lines">@@ -570,8 +570,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Eirunepe</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>d3c4df84162ebac445e1c2dcdb81f3b2</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>cc34b66260adda629311a54df088470b</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/El_Salvador</tzid>
</span><span class="lines">@@ -964,8 +964,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Porto_Acre</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>ce88b8461b7217e1d9ec8f4dfac34670</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>b2b04e3c9c1dab12a3764b99dd8536fc</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Porto_Velho</tzid>
</span><span class="lines">@@ -1006,10 +1006,10 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Rio_Branco</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
</ins><span class="cx"> <alias>America/Porto_Acre</alias>
</span><span class="cx"> <alias>Brazil/Acre</alias>
</span><del>- <md5>c41ff8b67906037ce014d42019e5831f</md5>
</del><ins>+ <md5>51273c4521cd00a8ecdcff336f0c9503</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>America/Rosario</tzid>
</span><span class="lines">@@ -1885,8 +1885,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Brazil/Acre</tzid>
</span><del>- <dtstamp>2011-10-05T11:50:21Z</dtstamp>
- <md5>b034140cdfc442b0f989f6a6dd6ec620</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>ad33124888f5c5dd9bed9317ef0bb953</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Brazil/DeNoronha</tzid>
</span><span class="lines">@@ -2641,8 +2641,8 @@
</span><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>Libya</tzid>
</span><del>- <dtstamp>2013-01-14T15:32:16Z</dtstamp>
- <md5>f514b497bd861c14aa21612f9101c50c</md5>
</del><ins>+ <dtstamp>2013-11-15T16:10:31Z</dtstamp>
+ <md5>e6ac54ea0ab33dd6fd125a71fc34cf46</md5>
</ins><span class="cx"> </timezone>
</span><span class="cx"> <timezone>
</span><span class="cx"> <tzid>MET</tzid>
</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):
+ """
+ Delete a shadowable property that has not been overridden by the sharee.
+ """
+
+ name = propertyName("shadow")
+
+ self.propertyStore1.setSpecialProperties((name,), ())
+ self.propertyStore2.setSpecialProperties((name,), ())
+
+ value1 = propertyValue("Hello, World1!")
+
+ 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("global")
</span></span></pre>
</div>
</div>
</body>
</html>