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