[CalendarServer-changes] [11057] CalendarServer/branches/users/cdaboo/store-scheduling

source_changes at macosforge.org source_changes at macosforge.org
Thu Apr 18 11:21:54 PDT 2013


Revision: 11057
          http://trac.calendarserver.org//changeset/11057
Author:   cdaboo at apple.com
Date:     2013-04-18 11:21:54 -0700 (Thu, 18 Apr 2013)
Log Message:
-----------
Checkpoint - getting unit tests to pass (currently ignored the cli tools). Moved CalDAV scheduling resource back into twistedcaldav.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_amppush.py
    CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_applepush.py
    CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_notifier.py
    CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/test/test_caldav.py
    CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/test/test_purge.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twext/web2/dav/test/util.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/directory.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/principal.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/test/test_calendar.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/test/test_principal.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/resource.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_calendarquery.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_collectioncontents.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_multiget.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_wrapping.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/util.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/base/propertystore/sql.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/base/propertystore/test/test_sql.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/scheduler.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/scheduler.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/common.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_sql.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/test/util.py

Added Paths:
-----------
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/__init__.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/__init__.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/resource.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/test/
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/test/__init__.py
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/test/test_resource.py

Removed Paths:
-------------
    CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_validation.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/resource.py
    CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/test/test_resource.py

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_amppush.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_amppush.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_amppush.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -16,10 +16,10 @@
 
 from calendarserver.push.amppush import AMPPushMaster, AMPPushNotifierProtocol
 from calendarserver.push.amppush import NotificationForID
-from twistedcaldav.test.util import TestCase
+from twistedcaldav.test.util import StoreTestCase
 from twisted.internet.task import Clock
 
-class AMPPushMasterTests(TestCase):
+class AMPPushMasterTests(StoreTestCase):
 
     def test_AMPPushMaster(self):
 

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_applepush.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_applepush.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_applepush.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -21,21 +21,14 @@
     ApplePushNotifierService, APNProviderProtocol
 )
 from calendarserver.push.util import validToken, TokenHistory
-from twistedcaldav.test.util import TestCase
+from twistedcaldav.test.util import StoreTestCase
 from twisted.internet.defer import inlineCallbacks, succeed
 from twisted.internet.task import Clock
-from txdav.common.datastore.test.util import buildStore, CommonCommonTests
 from txdav.common.icommondatastore import InvalidSubscriptionValues
 
-class ApplePushNotifierServiceTests(CommonCommonTests, TestCase):
+class ApplePushNotifierServiceTests(StoreTestCase):
 
     @inlineCallbacks
-    def setUp(self):
-        yield super(ApplePushNotifierServiceTests, self).setUp()
-        self.store = yield buildStore(self, None)
-
-
-    @inlineCallbacks
     def test_ApplePushNotifierService(self):
 
         settings = {
@@ -67,7 +60,7 @@
         }
 
         # Add subscriptions
-        txn = self.store.newTransaction()
+        txn = self._sqlCalendarStore.newTransaction()
 
         # Ensure empty values don't get through
         try:
@@ -116,7 +109,7 @@
         # Set up the service
         clock = Clock()
         service = (yield ApplePushNotifierService.makeService(settings,
-            self.store, testConnectorClass=TestConnector, reactor=clock))
+            self._sqlCalendarStore, testConnectorClass=TestConnector, reactor=clock))
         self.assertEquals(set(service.providers.keys()), set(["CalDAV", "CardDAV"]))
         self.assertEquals(set(service.feedbacks.keys()), set(["CalDAV", "CardDAV"]))
 
@@ -125,7 +118,7 @@
 
         # Notification arrives from calendar server
         dataChangedTimestamp = 1354815999
-        txn = self.store.newTransaction()
+        txn = self._sqlCalendarStore.newTransaction()
         yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/",
             dataChangedTimestamp=dataChangedTimestamp)
         yield txn.commit()
@@ -166,7 +159,7 @@
         # Reset sent data
         providerConnector.transport.data = None
         # Send notification while service is connected
-        txn = self.store.newTransaction()
+        txn = self._sqlCalendarStore.newTransaction()
         yield service.enqueue(txn, "/CalDAV/calendars.example.com/user01/calendar/")
         yield txn.commit()
         clock.advance(1) # so that first push is sent
@@ -208,7 +201,7 @@
         self.assertEquals(len(providerConnector.service.protocol.buffer), 1)
 
         # Prior to feedback, there are 2 subscriptions
-        txn = self.store.newTransaction()
+        txn = self._sqlCalendarStore.newTransaction()
         subscriptions = (yield txn.apnSubscriptionsByToken(token))
         yield txn.commit()
         self.assertEquals(len(subscriptions), 2)
@@ -247,7 +240,7 @@
         self.assertEquals(len(feedbackConnector.service.protocol.buffer), 1)
 
         # The second subscription should now be gone
-        txn = self.store.newTransaction()
+        txn = self._sqlCalendarStore.newTransaction()
         subscriptions = (yield txn.apnSubscriptionsByToken(token))
         yield txn.commit()
         self.assertEquals(subscriptions,
@@ -265,7 +258,7 @@
         self.assertTrue((id, token2) not in providerConnector.service.protocol.history.history)
 
         # All subscriptions for this token should now be gone
-        txn = self.store.newTransaction()
+        txn = self._sqlCalendarStore.newTransaction()
         subscriptions = (yield txn.apnSubscriptionsByToken(token2))
         yield txn.commit()
         self.assertEquals(subscriptions, [])
@@ -275,19 +268,19 @@
         #
 
         # Create two subscriptions, one old and one new
-        txn = self.store.newTransaction()
+        txn = self._sqlCalendarStore.newTransaction()
         now = int(time.time())
         yield txn.addAPNSubscription(token2, key1, now - 2 * 24 * 60 * 60, uid, userAgent, ipAddr) # old
         yield txn.addAPNSubscription(token2, key2, now, uid, userAgent, ipAddr) # recent
         yield txn.commit()
 
         # Purge old subscriptions
-        txn = self.store.newTransaction()
+        txn = self._sqlCalendarStore.newTransaction()
         yield txn.purgeOldAPNSubscriptions(now - 60 * 60)
         yield txn.commit()
 
         # Check that only the recent subscription remains
-        txn = self.store.newTransaction()
+        txn = self._sqlCalendarStore.newTransaction()
         subscriptions = (yield txn.apnSubscriptionsByToken(token2))
         yield txn.commit()
         self.assertEquals(len(subscriptions), 1)

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_notifier.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_notifier.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/push/test/test_notifier.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -14,13 +14,12 @@
 # limitations under the License.
 ##
 
-from twistedcaldav.test.util import TestCase
+from twistedcaldav.test.util import StoreTestCase
 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 twistedcaldav.config import ConfigDict
-from txdav.common.datastore.test.util import buildStore
 
 
 class StubService(object):
@@ -38,7 +37,7 @@
 
 
 
-class PushDistributorTests(TestCase):
+class PushDistributorTests(StoreTestCase):
 
     @inlineCallbacks
     def test_enqueue(self):
@@ -95,20 +94,19 @@
 
 
 
-class PushNotificationWorkTests(TestCase):
+class PushNotificationWorkTests(StoreTestCase):
 
     @inlineCallbacks
     def test_work(self):
-        self.store = yield buildStore(self, None)
 
         pushDistributor = StubDistributor()
 
         def decorateTransaction(txn):
             txn._pushDistributor = pushDistributor
 
-        self.store.callWithNewTransactions(decorateTransaction)
+        self._sqlCalendarStore.callWithNewTransactions(decorateTransaction)
 
-        txn = self.store.newTransaction()
+        txn = self._sqlCalendarStore.newTransaction()
         wp = (yield txn.enqueue(PushNotificationWork,
             pushID="/CalDAV/localhost/foo/",
         ))
@@ -117,7 +115,7 @@
         self.assertEquals(pushDistributor.history, ["/CalDAV/localhost/foo/"])
 
         pushDistributor.reset()
-        txn = self.store.newTransaction()
+        txn = self._sqlCalendarStore.newTransaction()
         wp = (yield txn.enqueue(PushNotificationWork,
             pushID="/CalDAV/localhost/bar/",
         ))

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/test/test_caldav.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/test/test_caldav.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tap/test/test_caldav.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -55,7 +55,7 @@
 from twistedcaldav.directory.calendar import DirectoryCalendarHomeProvisioningResource
 from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
 
-from twistedcaldav.test.util import TestCase, CapturingProcessProtocol
+from twistedcaldav.test.util import StoreTestCase, CapturingProcessProtocol
 
 from calendarserver.tap.caldav import (
     CalDAVOptions, CalDAVServiceMaker, CalDAVService, GroupOwnedUNIXServer,
@@ -93,6 +93,7 @@
         self.childFDs = childFDs
 
 
+
 class InMemoryProcessSpawner(Clock):
     """
     Stub out L{IReactorProcess} and L{IReactorClock} so that we can examine the
@@ -149,12 +150,15 @@
     def checkDirectory(self, *args, **kwargs):
         pass
 
+
     def checkFile(self, *args, **kwargs):
         pass
 
+
     def checkDirectories(self, *args, **kwargs):
         pass
 
+
     def loadConfiguration(self):
         """
         Simple wrapper to avoid printing during test runs.
@@ -170,26 +174,30 @@
             )
 
 
-class CalDAVOptionsTest (TestCase):
+
+class CalDAVOptionsTest (StoreTestCase):
     """
     Test various parameters of our usage.Options subclass
     """
+    @inlineCallbacks
     def setUp(self):
         """
         Set up our options object, giving it a parent, and forcing the
         global config to be loaded from defaults.
         """
-        TestCase.setUp(self)
+        yield super(CalDAVOptionsTest, self).setUp()
         self.config = TestCalDAVOptions()
         self.config.parent = Options()
         self.config.parent["uid"] = 0
         self.config.parent["gid"] = 0
         self.config.parent["nodaemon"] = False
 
+
     def tearDown(self):
         config.setDefaults(DEFAULT_CONFIG)
         config.reload()
 
+
     def test_overridesConfig(self):
         """
         Test that values on the command line's -o and --option options
@@ -223,6 +231,7 @@
 
         self.assertRaises(UsageError, self.config.parseOptions, argv)
 
+
     def test_setsParent(self):
         """
         Test that certain values are set on the parent (i.e. twistd's
@@ -271,6 +280,7 @@
             myConfig.Authentication.Basic.Enabled
         )
 
+
     def test_specifyDictPath(self):
         """
         Test that we can specify command line overrides to leafs using
@@ -289,14 +299,17 @@
 
         self.assertEquals(config.MultiProcess["ProcessCount"], 102)
 
-class BaseServiceMakerTests(TestCase):
+
+
+class BaseServiceMakerTests(StoreTestCase):
     """
     Utility class for ServiceMaker tests.
     """
     configOptions = None
 
+    @inlineCallbacks
     def setUp(self):
-        TestCase.setUp(self)
+        yield super(BaseServiceMakerTests, self).setUp()
         self.options = TestCalDAVOptions()
         self.options.parent = Options()
         self.options.parent["gid"] = None
@@ -324,13 +337,13 @@
             "type": "twistedcaldav.directory.augment.AugmentXMLDB"
         }
 
-        self.config.UseDatabase    = False
-        self.config.ServerRoot     = self.mktemp()
-        self.config.ConfigRoot     = "config"
-        self.config.ProcessType    = "Single"
-        self.config.SSLPrivateKey  = pemFile
+        self.config.UseDatabase = False
+        self.config.ServerRoot = self.mktemp()
+        self.config.ConfigRoot = "config"
+        self.config.ProcessType = "Single"
+        self.config.SSLPrivateKey = pemFile
         self.config.SSLCertificate = pemFile
-        self.config.EnableSSL      = True
+        self.config.EnableSSL = True
         self.config.Memcached.Pools.Default.ClientEnabled = False
         self.config.Memcached.Pools.Default.ServerEnabled = False
         self.config.DirectoryAddressBook.Enabled = False
@@ -415,7 +428,7 @@
 
 
 
-class SocketGroupOwnership(TestCase):
+class SocketGroupOwnership(StoreTestCase):
     """
     Tests for L{GroupOwnedUNIXServer}.
     """
@@ -523,11 +536,15 @@
         self.writeConfig()
         class NotAStore(object):
             queuer = LocalQueuer(None)
+            def __init__(self, directory):
+                self.directory = directory
             def newTransaction(self):
                 return None
             def callWithNewTransactions(self, x):
                 pass
-        store = NotAStore()
+            def directoryService(self):
+                return self.directory
+        store = NotAStore(self.directory)
         def something(proposal):
             pass
         store.queuer.callWithNewProposals(something)
@@ -578,6 +595,7 @@
             "%s is not a CalDAVService" % (service,)
         )
 
+
     def test_defaultListeners(self):
         """
         Test that the Slave service has sub services with the
@@ -631,6 +649,7 @@
             context.certificateFileName,
         )
 
+
     def test_noSSL(self):
         """
         Test the single service to make sure there is no SSL Service when SSL
@@ -646,6 +665,7 @@
             [s.__class__ for s in service.services]
         )
 
+
     def test_noHTTP(self):
         """
         Test the single service to make sure there is no TCPServer when
@@ -661,6 +681,7 @@
             [s.__class__ for s in service.services]
         )
 
+
     def test_singleBindAddresses(self):
         """
         Test that the TCPServer and SSLServers are bound to the proper address
@@ -674,6 +695,7 @@
             if isinstance(s, (internet.TCPServer, internet.SSLServer)):
                 self.assertEquals(s.kwargs["interface"], "127.0.0.1")
 
+
     def test_multipleBindAddresses(self):
         """
         Test that the TCPServer and SSLServers are bound to the proper
@@ -712,6 +734,7 @@
         self.assertEquals(len(tcpServers), 0)
         self.assertEquals(len(sslServers), 0)
 
+
     def test_listenBacklog(self):
         """
         Test that the backlog arguments is set in TCPServer and SSLServers
@@ -725,6 +748,7 @@
                 self.assertEquals(s.kwargs["backlog"], 1024)
 
 
+
 class ServiceHTTPFactoryTests(BaseServiceMakerTests):
     """
     Test the configuration of the initial resource hierarchy of the
@@ -759,6 +783,7 @@
         self.assertEquals(len(expectedSchemes),
                           len(authWrapper.credentialFactories))
 
+
     def test_servicePrincipalNone(self):
         """
         Test that the Kerberos principal look is attempted if the principal is empty.
@@ -770,8 +795,9 @@
 
         authWrapper = site.resource.resource
 
-        self.assertFalse(authWrapper.credentialFactories.has_key("negotiate"))
+        self.assertFalse("negotiate" in authWrapper.credentialFactories)
 
+
     def test_servicePrincipal(self):
         """
         Test that the kerberos realm is the realm portion of a principal
@@ -788,6 +814,7 @@
         self.assertEquals(ncf.service, "http at HELLO")
         self.assertEquals(ncf.realm, "bob")
 
+
     def test_AuthWrapperPartialEnabled(self):
         """
         Test that the expected credential factories exist when
@@ -795,7 +822,7 @@
         enabled.
         """
 
-        self.config.Authentication.Basic.Enabled    = False
+        self.config.Authentication.Basic.Enabled = False
         self.config.Authentication.Kerberos.Enabled = False
 
         self.writeConfig()
@@ -813,6 +840,7 @@
             len(authWrapper.credentialFactories)
         )
 
+
     def test_LogWrapper(self):
         """
         Test the configuration of the log wrapper
@@ -823,6 +851,7 @@
                 site.resource,
                 LogWrapperResource))
 
+
     def test_rootResource(self):
         """
         Test the root resource
@@ -832,6 +861,7 @@
 
         self.failUnless(isinstance(root, RootResource))
 
+
     def test_principalResource(self):
         """
         Test the principal resource
@@ -844,6 +874,7 @@
             DirectoryPrincipalProvisioningResource
         ))
 
+
     def test_calendarResource(self):
         """
         Test the calendar resource
@@ -863,7 +894,7 @@
 <dict>
     <key>users</key>
     <array>
-       	<dict>
+        <dict>
             <key>password</key>
             <string>superuser</string>
             <key>username</key>
@@ -892,6 +923,7 @@
 
         self.assertEquals(principals.directory, calendars.directory)
 
+
     def test_aggregateDirectory(self):
         """
         Assert that the base directory service is actually
@@ -955,6 +987,7 @@
         return 'Dummy'
 
 
+
 class ScriptProcessObject(DummyProcessObject):
     """
     Simple stub for the Process Object API that will run a test script.
@@ -971,9 +1004,7 @@
 
 
 
-
-
-class DelayedStartupProcessMonitorTests(TestCase):
+class DelayedStartupProcessMonitorTests(StoreTestCase):
     """
     Test cases for L{DelayedStartupProcessMonitor}.
     """
@@ -1068,13 +1099,13 @@
 
         # Most arguments here will be ignored, so these are bogus values.
         slave = TwistdSlaveProcess(
-            twistd        = "bleh",
-            tapname       = "caldav",
-            configFile    = "/does/not/exist",
-            id            = 10,
-            interfaces    = '127.0.0.1',
-            inheritFDs    = [3, 7],
-            inheritSSLFDs = [19, 25],
+            twistd="bleh",
+            tapname="caldav",
+            configFile="/does/not/exist",
+            id=10,
+            interfaces='127.0.0.1',
+            inheritFDs=[3, 7],
+            inheritSSLFDs=[19, 25],
         )
 
         dspm.addProcessObject(slave, {})
@@ -1121,12 +1152,12 @@
         dspm = DelayedStartupProcessMonitor(imps)
         # Most arguments here will be ignored, so these are bogus values.
         slave = TwistdSlaveProcess(
-            twistd     = "bleh",
-            tapname    = "caldav",
-            configFile = "/does/not/exist",
-            id         = 10,
-            interfaces = '127.0.0.1',
-            metaSocket = FakeDispatcher().addSocket()
+            twistd="bleh",
+            tapname="caldav",
+            configFile="/does/not/exist",
+            id=10,
+            interfaces='127.0.0.1',
+            metaSocket=FakeDispatcher().addSocket()
         )
 
         dspm.addProcessObject(slave, {})
@@ -1134,7 +1165,7 @@
         oneProcessTransport = imps.waitForOneProcess()
         self.assertIn("MetaFD=4", oneProcessTransport.args)
         self.assertEquals(
-            oneProcessTransport.args[oneProcessTransport.args.index("MetaFD=4")-1],
+            oneProcessTransport.args[oneProcessTransport.args.index("MetaFD=4") - 1],
             '-o',
             "MetaFD argument was not passed as an option"
         )
@@ -1155,12 +1186,12 @@
         sampleCounter = range(0, 5)
         for counter in sampleCounter:
             slave = TwistdSlaveProcess(
-                twistd     = "bleh",
-                tapname    = "caldav",
-                configFile = "/does/not/exist",
-                id         = counter * 10,
-                interfaces = '127.0.0.1',
-                metaSocket = FakeDispatcher().addSocket()
+                twistd="bleh",
+                tapname="caldav",
+                configFile="/does/not/exist",
+                id=counter * 10,
+                interfaces='127.0.0.1',
+                metaSocket=FakeDispatcher().addSocket()
             )
             dspm.addProcessObject(slave, {"SAMPLE_ENV_COUNTER": str(counter)})
         dspm.startService()
@@ -1179,7 +1210,7 @@
     def __init__(self, n):
         self.fd = n
 
-    
+
     def fileno(self):
         return self.fd
 
@@ -1194,7 +1225,7 @@
 
 
 
-class TwistdSlaveProcessTests(TestCase):
+class TwistdSlaveProcessTests(StoreTestCase):
     """
     Tests for L{TwistdSlaveProcess}.
     """
@@ -1212,11 +1243,8 @@
 
 
 
+class ReExecServiceTests(StoreTestCase):
 
-
-
-class ReExecServiceTests(TestCase):
-
     @inlineCallbacks
     def test_reExecService(self):
         """
@@ -1239,7 +1267,8 @@
         self.assertEquals(output.count("STOP"), 2)
 
 
-class SystemIDsTests(TestCase):
+
+class SystemIDsTests(StoreTestCase):
     """
     Verifies the behavior of calendarserver.tap.caldav.getSystemIDs
     """
@@ -1281,6 +1310,7 @@
             }
         )
 
+
     def test_getSystemIDs_UserNameNotFound(self):
         """
         If userName is passed in but is not found on the system, raise a
@@ -1289,6 +1319,7 @@
         self.assertRaises(ConfigurationError, self._wrappedFunction(),
             "nonexistent", "exists")
 
+
     def test_getSystemIDs_GroupNameNotFound(self):
         """
         If groupName is passed in but is not found on the system, raise a
@@ -1297,12 +1328,14 @@
         self.assertRaises(ConfigurationError, self._wrappedFunction(),
             "exists", "nonexistent")
 
+
     def test_getSystemIDs_NamesNotSpecified(self):
         """
         If names are not provided, use the IDs of the process
         """
         self.assertEquals(self._wrappedFunction()("", ""), (44, 45))
 
+
     def test_getSystemIDs_NamesSpecified(self):
         """
         If names are provided, use the IDs corresponding to those names

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/test/test_purge.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/test/test_purge.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/calendarserver/tools/test/test_purge.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -15,19 +15,17 @@
 ##
 
 
-from calendarserver.tap.util import getRootResource
 from calendarserver.tools.purge import PurgePrincipalService
 
 from twistedcaldav.config import config
 from twistedcaldav.ical import Component
-from twistedcaldav.test.util import TestCase
+from twistedcaldav.test.util import StoreTestCase
 
 from pycalendar.datetime import PyCalendarDateTime
 from pycalendar.timezone import PyCalendarTimezone
 
-from twisted.trial import unittest
 from twisted.internet.defer import inlineCallbacks
-from txdav.common.datastore.test.util import buildStore, populateCalendarsFrom, CommonCommonTests
+from txdav.common.datastore.test.util import populateCalendarsFrom
 from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
 
 from twext.web2.http_headers import MimeType
@@ -227,7 +225,7 @@
 
 
 
-class CancelEventTestCase(TestCase):
+class CancelEventTestCase(StoreTestCase):
 
     def test_cancelRepeating(self):
         # A repeating event where purged CUA is organizer
@@ -779,7 +777,7 @@
 
 
 
-class PurgePrincipalTests(CommonCommonTests, unittest.TestCase):
+class PurgePrincipalTests(StoreTestCase):
     """
     Tests for purging the data belonging to a given principal
     """
@@ -808,10 +806,6 @@
 
     @inlineCallbacks
     def setUp(self):
-        yield super(PurgePrincipalTests, self).setUp()
-        self._sqlCalendarStore = yield buildStore(self, self.notifierFactory)
-        yield self.populate()
-
         self.patch(config.DirectoryService.params, "xmlFile",
             os.path.join(
                 os.path.dirname(__file__), "purge", "accounts.xml"
@@ -822,8 +816,7 @@
                 os.path.dirname(__file__), "purge", "resources.xml"
             )
         )
-        self.rootResource = getRootResource(config, self._sqlCalendarStore)
-        self.directory = self.rootResource.getDirectory()
+        yield super(PurgePrincipalTests, self).setUp()
 
         txn = self._sqlCalendarStore.newTransaction()
 
@@ -863,13 +856,6 @@
         self.notifierFactory.reset()
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlCalendarStore
-
-
     @inlineCallbacks
     def test_purgeUIDs(self):
         """

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twext/web2/dav/test/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twext/web2/dav/test/util.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twext/web2/dav/test/util.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -7,10 +7,10 @@
 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 # copies of the Software, and to permit persons to whom the Software is
 # furnished to do so, subject to the following conditions:
-# 
+#
 # The above copyright notice and this permission notice shall be included in all
 # copies or substantial portions of the Software.
-# 
+#
 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -58,7 +58,7 @@
     associated logic with the C{chanRequest} attribute).
     """
 
-    clientproto = (1,1)
+    clientproto = (1, 1)
 
     def __init__(self, site, method, uri, headers=None, content=None):
         if not headers:
@@ -80,7 +80,10 @@
         self.host = 'localhost'
         self.port = 8080
 
+
     def writeResponse(self, response):
+        if self.chanRequest:
+            self.chanRequest.writeHeaders(response.code, response.headers)
         return response
 
 
@@ -94,6 +97,7 @@
     def __init__(self, resource):
         self._dict = {}
 
+
     def get(self, qname):
         try:
             property = self._dict[qname]
@@ -106,21 +110,27 @@
         doc = element.WebDAVDocument.fromString(property)
         return doc.root_element
 
+
     def set(self, property):
         self._dict[property.qname()] = property.toxml()
 
+
     def delete(self, qname):
         try:
             del(self._dict[qname])
         except KeyError:
             pass
 
+
     def contains(self, qname):
         return qname in self._dict
 
+
     def list(self):
         return self._dict.keys()
 
+
+
 class TestFile (DAVFile):
     _cachedPropertyStores = {}
 
@@ -135,9 +145,12 @@
 
         return self._dead_properties
 
+
     def parent(self):
         return TestFile(self.fp.parent())
 
+
+
 class TestCase (unittest.TestCase):
     resource_class = TestFile
 
@@ -193,7 +206,7 @@
             if os.path.isfile(os.path.join(src, f))
         ]
 
-        for dirname in (docroot,) + dirnames[3:8+1]:
+        for dirname in (docroot,) + dirnames[3:8 + 1]:
             for filename in filenames[:5]:
                 copy(filename, dirname)
         return docroot
@@ -207,6 +220,7 @@
 
         return self._docroot
 
+
     def _setDocumentRoot(self, value):
         self._docroot = value
 
@@ -219,6 +233,7 @@
             self._site = Site(rootresource)
         return self._site
 
+
     def _setSite(self, site):
         self._site = site
 
@@ -228,6 +243,7 @@
         unittest.TestCase.setUp(self)
         TestFile._cachedPropertyStores = {}
 
+
     def tearDown(self):
         unittest.TestCase.tearDown(self)
 
@@ -238,7 +254,7 @@
         URI.
         """
         path = mkdtemp(prefix=prefix + "_", dir=self.docroot)
-        uri  = joinURL("/", url_quote(os.path.basename(path))) + "/"
+        uri = joinURL("/", url_quote(os.path.basename(path))) + "/"
 
         return (os.path.abspath(path), uri)
 
@@ -319,7 +335,6 @@
 
 
 
-
 class Site:
     # FIXME: There is no ISite interface; there should be.
     # implements(ISite)
@@ -327,6 +342,8 @@
     def __init__(self, resource):
         self.resource = resource
 
+
+
 def dircmp(dir1, dir2):
     dc = DirCompare(dir1, dir2)
     return bool(
@@ -335,12 +352,15 @@
         dc.common_funny or dc.funny_files
     )
 
+
+
 def serialize(f, work):
     d = Deferred()
 
     def oops(error):
         d.errback(error)
 
+
     def do_serialize(_):
         try:
             args = work.next()

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/directory.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/directory.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -114,6 +114,14 @@
         self.realmName = realmName
 
 
+    def getPrincipalPath(self):
+        return getattr(self, "principalPath", None)
+
+
+    def setPrincipalPath(self, principalPath):
+        self.principalPath = principalPath
+
+
     def available(self):
         """
         By default, the directory is available.  This may return a boolean or a
@@ -1078,8 +1086,14 @@
             ["mailto:%s" % (emailAddress,)
              for emailAddress in self.emailAddresses]
         )
+        path = self.service.getPrincipalPath()
         if self.guid:
             cuas.add("urn:uuid:%s" % (self.guid,))
+            if path:
+                cuas.add("/%s/__uids__/%s/" % (path, self.guid,))
+        if path:
+            for shortName in self.shortNames:
+                cuas.add("/%s/%s/%s/" % (path, self.recordType, shortName,))
 
         return frozenset(cuas)
 
@@ -1171,7 +1185,7 @@
 
 
     def displayName(self):
-        return self.record.fullName if self.record.fullName else self.record.shortNames[0]
+        return self.fullName if self.fullName else self.shortNames[0]
 
 
     def isLoginEnabled(self):
@@ -1320,7 +1334,7 @@
     def canAutoSchedule(self, organizer):
         if config.Scheduling.Options.AutoSchedule.Enabled:
             if (config.Scheduling.Options.AutoSchedule.Always or
-                self.getAutoSchedule() or
+                self.autoSchedule or
                 self.autoAcceptFromOrganizer(organizer)):
                 if (self.getCUType() != "INDIVIDUAL" or
                     config.Scheduling.Options.AutoSchedule.AllowUsers):

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/principal.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/principal.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -1203,7 +1203,7 @@
 
 
     def calendarUserAddresses(self):
-        return self.record.calendarUserAddresses()
+        return self.record.calendarUserAddresses
 
 
     def htmlElement(self):

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/test/test_calendar.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/test/test_calendar.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/test/test_calendar.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -19,41 +19,18 @@
 
 from twistedcaldav import caldavxml
 
-from twistedcaldav.test.util import TestCase
-from twext.web2.test.test_server import SimpleRequest
-from twistedcaldav.directory.util import transactionFromRequest, NotFoundResource
+from twistedcaldav.test.util import StoreTestCase, SimpleStoreRequest
+from twistedcaldav.directory.util import NotFoundResource
 
-class ProvisionedCalendars (TestCase):
+class ProvisionedCalendars (StoreTestCase):
     """
     Directory service provisioned principals.
     """
 
-    @inlineCallbacks
-    def setUp(self):
-        yield super(ProvisionedCalendars, self).setUp()
-
-        self.createStockDirectoryService()
-        self.setupCalendars()
-
-
     def oneRequest(self, uri):
-        req = self._cleanupRequest = SimpleRequest(self.site, "GET", uri)
-        return req
+        return SimpleStoreRequest(self, "GET", uri)
 
 
-    def tearDown(self):
-        """
-        If the request started by this test has a transaction, commit it.
-        Otherwise, don't bother.
-        """
-        class JustForCleanup(object):
-            def newTransaction(self, *whatever):
-                return self
-            def commit(self):
-                return
-        return transactionFromRequest(self._cleanupRequest, JustForCleanup()).commit()
-
-
     def test_NonExistentCalendarHome(self):
         """
         Requests for missing homes and principals should return
@@ -67,7 +44,9 @@
         request = self.oneRequest("/calendars/users/12345/")
         d = request.locateResource(request.uri)
         d.addCallback(_response)
+        return d
 
+
     def test_ExistentCalendarHome(self):
 
         def _response(resource):
@@ -77,7 +56,9 @@
         request = self.oneRequest("/calendars/users/wsanchez/")
         d = request.locateResource(request.uri)
         d.addCallback(_response)
+        return d
 
+
     def test_ExistentCalendar(self):
 
         def _response(resource):
@@ -87,7 +68,9 @@
         request = self.oneRequest("/calendars/users/wsanchez/calendar/")
         d = request.locateResource(request.uri)
         d.addCallback(_response)
+        return d
 
+
     def test_ExistentInbox(self):
 
         def _response(resource):
@@ -97,7 +80,9 @@
         request = self.oneRequest("/calendars/users/wsanchez/inbox/")
         d = request.locateResource(request.uri)
         d.addCallback(_response)
+        return d
 
+
     @inlineCallbacks
     def test_CalendarTranspProperty(self):
 
@@ -111,7 +96,7 @@
         inbox = (yield request.locateResource("/calendars/users/wsanchez/inbox/"))
         if inbox is None:
             self.fail("Incorrect response to GET on existent inbox.")
-        
+
         # Provisioned calendar has default opaque property
         transp = (yield calendar.hasProperty(caldavxml.ScheduleCalendarTransp, request))
         self.assertTrue(transp)
@@ -173,4 +158,3 @@
 
         transp = (yield calendar.readProperty(caldavxml.ScheduleCalendarTransp, request))
         self.assertEqual(transp, caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
-

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/test/test_principal.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/directory/test/test_principal.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -69,6 +69,7 @@
         for directory in self.directoryServices:
             name = directory.__class__.__name__
             url = "/" + name + "/"
+            directory.setPrincipalPath(name)
 
             provisioningResource = DirectoryPrincipalProvisioningResource(url, directory)
 
@@ -138,14 +139,16 @@
                     principalCollections = recordResource.principalCollections()
                     self.assertEquals(set((provisioningURL,)), set(pc.principalCollectionURL() for pc in principalCollections))
 
+
     def test_allRecords(self):
         """
         Test of a test routine...
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabled:
                 self.assertEquals(recordResource.record, record)
 
+
     ##
     # DirectoryPrincipalProvisioningResource
     ##
@@ -154,7 +157,7 @@
         """
         DirectoryPrincipalProvisioningResource.principalForShortName()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, recordType, _ignore_recordResource, record in self._allRecords():
             principal = provisioningResource.principalForShortName(recordType, record.shortNames[0])
             if record.enabled:
                 self.failIf(principal is None)
@@ -162,6 +165,7 @@
             else:
                 self.failIf(principal is not None)
 
+
     def test_principalForUser(self):
         """
         DirectoryPrincipalProvisioningResource.principalForUser()
@@ -177,6 +181,7 @@
                 else:
                     self.failIf(userResource is not None)
 
+
     def test_principalForAuthID(self):
         """
         DirectoryPrincipalProvisioningResource.principalForAuthID()
@@ -193,11 +198,12 @@
                 else:
                     self.failIf(userResource is not None)
 
+
     def test_principalForUID(self):
         """
         DirectoryPrincipalProvisioningResource.principalForUID()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, _ignore_recordType, _ignore_recordResource, record in self._allRecords():
             principal = provisioningResource.principalForUID(record.uid)
             if record.enabled:
                 self.failIf(principal is None)
@@ -205,11 +211,12 @@
             else:
                 self.failIf(principal is not None)
 
+
     def test_principalForRecord(self):
         """
         DirectoryPrincipalProvisioningResource.principalForRecord()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, _ignore_recordType, _ignore_recordResource, record in self._allRecords():
             principal = provisioningResource.principalForRecord(record)
             if record.enabled:
                 self.failIf(principal is None)
@@ -217,14 +224,15 @@
             else:
                 self.failIf(principal is not None)
 
+
     def test_principalForCalendarUserAddress(self):
         """
         DirectoryPrincipalProvisioningResource.principalForCalendarUserAddress()
         """
         for (
-            provisioningResource, recordType, recordResource, record
+            provisioningResource, _ignore_recordType, recordResource, record
         ) in self._allRecords():
-        
+
             test_items = tuple(record.calendarUserAddresses)
             if recordResource:
                 principalURL = recordResource.principalURL()
@@ -270,12 +278,13 @@
             None
         )
 
+
     @inlineCallbacks
     def test_enabledForCalendaring(self):
         """
         DirectoryPrincipalProvisioningResource.principalForCalendarUserAddress()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, _ignore_recordType, _ignore_recordResource, record in self._allRecords():
             principal = provisioningResource.principalForRecord(record)
             if record.enabled:
                 self.failIf(principal is None)
@@ -295,7 +304,7 @@
             def hasProperty(property):
                 self.assertTrue(property in principal.liveProperties())
                 yield principal.readProperty(property, None)
-                
+
             @inlineCallbacks
             def doesNotHaveProperty(property):
                 self.assertTrue(property not in principal.liveProperties())
@@ -307,7 +316,7 @@
                     self.fail("Wrong exception type")
                 else:
                     self.fail("No exception principal: %s, property %s" % (principal, property,))
-                
+
             if record.enabledForCalendaring:
                 yield hasProperty((caldav_namespace, "calendar-home-set"))
                 yield hasProperty((caldav_namespace, "calendar-user-address-set"))
@@ -332,16 +341,17 @@
             else:
                 yield doesNotHaveProperty(carddavxml.AddressBookHomeSet.qname())
 
+
     def test_enabledAsOrganizer(self):
         """
         DirectoryPrincipalProvisioningResource.principalForCalendarUserAddress()
         """
-        
+
         ok_types = (
             DirectoryService.recordType_users,
         )
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
-            
+        for provisioningResource, recordType, _ignore_recordResource, record in self._allRecords():
+
             if record.enabledForCalendaring:
                 principal = provisioningResource.principalForRecord(record)
                 self.failIf(principal is None)
@@ -356,13 +366,14 @@
             DirectoryService.recordType_locations,
             DirectoryService.recordType_resources,
         )
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
-            
+        for provisioningResource, recordType, _ignore_recordResource, record in self._allRecords():
+
             if record.enabledForCalendaring:
                 principal = provisioningResource.principalForRecord(record)
                 self.failIf(principal is None)
                 self.assertEqual(principal.enabledAsOrganizer(), recordType in ok_types)
 
+
     # FIXME: Run DirectoryPrincipalProvisioningResource tests on DirectoryPrincipalTypeProvisioningResource also
 
     ##
@@ -374,52 +385,57 @@
         Each DirectoryPrincipalResource should have a cacheNotifier attribute
         that is an instance of DisabledCacheNotifier
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabled:
                 self.failUnless(isinstance(recordResource.cacheNotifier,
                                            DisabledCacheNotifier))
 
+
     def test_displayName(self):
         """
         DirectoryPrincipalResource.displayName()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabled:
                 self.failUnless(recordResource.displayName())
 
+
     @inlineCallbacks
     def test_groupMembers(self):
         """
         DirectoryPrincipalResource.groupMembers()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabled:
                 members = yield recordResource.groupMembers()
                 self.failUnless(set(record.members()).issubset(set(r.record for r in members)))
 
+
     @inlineCallbacks
     def test_groupMemberships(self):
         """
         DirectoryPrincipalResource.groupMemberships()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabled:
                 memberships = yield recordResource.groupMemberships()
                 self.failUnless(set(record.groups()).issubset(set(r.record for r in memberships if hasattr(r, "record"))))
 
+
     def test_principalUID(self):
         """
         DirectoryPrincipalResource.principalUID()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabled:
                 self.assertEquals(record.guid, recordResource.principalUID())
 
+
     def test_calendarUserAddresses(self):
         """
         DirectoryPrincipalResource.calendarUserAddresses()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabledForCalendaring:
                 self.failUnless(
                     (
@@ -432,20 +448,22 @@
                 record.enabledForCalendaring = False
                 self.failIf(recordResource.calendarUserAddresses())
 
+
     def test_canonicalCalendarUserAddress(self):
         """
         DirectoryPrincipalResource.canonicalCalendarUserAddress()
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabledForCalendaring:
                 self.failUnless(recordResource.canonicalCalendarUserAddress().startswith("urn:uuid:"))
 
+
     def test_addressBookHomeURLs(self):
         """
         DirectoryPrincipalResource.addressBookHomeURLs(),
         """
         # No addressbook home provisioner should result in no addressbook homes.
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabledForAddressBooks:
                 self.failIf(tuple(recordResource.addressBookHomeURLs()))
 
@@ -460,7 +478,7 @@
             os.mkdir(path)
 
             # Need a data store
-            _newStore = CommonDataStore(path, None, True, False)
+            _newStore = CommonDataStore(path, None, None, True, False)
 
             provisioningResource = DirectoryAddressBookHomeProvisioningResource(
                 directory,
@@ -471,7 +489,7 @@
             addressBookRootResources[directory.__class__.__name__] = provisioningResource
 
         # AddressBook home provisioners should result in addressBook homes.
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabledForAddressBooks:
                 homeURLs = tuple(recordResource.addressBookHomeURLs())
                 self.failUnless(homeURLs)
@@ -487,6 +505,7 @@
                 for homeURL in homeURLs:
                     self.failUnless(homeURL.startswith(addressBookRootURL))
 
+
     def test_calendarHomeURLs(self):
         """
         DirectoryPrincipalResource.calendarHomeURLs(),
@@ -494,7 +513,7 @@
         DirectoryPrincipalResource.scheduleOutboxURL()
         """
         # No calendar home provisioner should result in no calendar homes.
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabledForCalendaring:
                 self.failIf(tuple(recordResource.calendarHomeURLs()))
                 self.failIf(recordResource.scheduleInboxURL())
@@ -511,7 +530,7 @@
             os.mkdir(path)
 
             # Need a data store
-            _newStore = CommonDataStore(path, None, True, False)
+            _newStore = CommonDataStore(path, None, None, True, False)
 
             provisioningResource = DirectoryCalendarHomeProvisioningResource(
                 directory,
@@ -522,7 +541,7 @@
             calendarRootResources[directory.__class__.__name__] = provisioningResource
 
         # Calendar home provisioners should result in calendar homes.
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabledForCalendaring:
                 homeURLs = tuple(recordResource.calendarHomeURLs())
                 self.failUnless(homeURLs)
@@ -557,19 +576,20 @@
                 self.failIf(inboxURL)
                 self.failIf(outboxURL)
 
+
     def test_canAutoSchedule(self):
         """
         DirectoryPrincipalResource.canAutoSchedule()
         """
 
         # Set all resources and locations to auto-schedule, plus one user
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, recordType, recordResource, record in self._allRecords():
             if record.enabledForCalendaring:
                 if recordType in ("locations", "resources") or record.uid == "cdaboo":
                     recordResource.record.autoSchedule = True
 
         # Default state - resources and locations, enabled, others not
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, recordType, recordResource, record in self._allRecords():
             if record.enabledForCalendaring:
                 if recordType in ("locations", "resources"):
                     self.assertTrue(recordResource.canAutoSchedule())
@@ -578,7 +598,7 @@
 
         # Set config to allow users
         self.patch(config.Scheduling.Options.AutoSchedule, "AllowUsers", True)
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, recordType, recordResource, record in self._allRecords():
             if record.enabledForCalendaring:
                 if recordType in ("locations", "resources") or record.uid == "cdaboo":
                     self.assertTrue(recordResource.canAutoSchedule())
@@ -587,7 +607,7 @@
 
         # Set config to disallow all
         self.patch(config.Scheduling.Options.AutoSchedule, "Enabled", False)
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, recordType, recordResource, record in self._allRecords():
             if record.enabledForCalendaring:
                 self.assertFalse(recordResource.canAutoSchedule())
 
@@ -600,7 +620,7 @@
         # Location "apollo" has an auto-accept group ("both_coasts") set in augments.xml,
         # therefore any organizer in that group should be able to auto schedule
 
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.uid == "apollo":
 
                 # No organizer
@@ -617,11 +637,12 @@
         """
         Default access controls for principals.
         """
-        for provisioningResource, recordType, recordResource, record in self._allRecords():
+        for _ignore_provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
             if record.enabled:
                 for args in _authReadOnlyPrivileges(self, recordResource, recordResource.principalURL()):
                     yield self._checkPrivileges(*args)
 
+
     @inlineCallbacks
     def test_defaultAccessControlList_provisioners(self):
         """
@@ -641,6 +662,7 @@
                 for args in _authReadOnlyPrivileges(self, typeResource, typeResource.principalCollectionURL()):
                     yield self._checkPrivileges(*args)
 
+
     def test_propertyToField(self):
 
         class stubElement(object):
@@ -677,6 +699,7 @@
                 (field, converted)
             )
 
+
     def _allRecords(self):
         """
         @return: an iterable of tuples
@@ -696,6 +719,7 @@
                     recordResource = provisioningResource.principalForRecord(record)
                     yield provisioningResource, recordType, recordResource, record
 
+
     def _checkPrivileges(self, resource, url, principal, privilege, allowed):
         request = SimpleRequest(self.site, "GET", "/")
 
@@ -720,14 +744,16 @@
         d.addCallback(gotResource)
         return d
 
+
+
 def _authReadOnlyPrivileges(self, resource, url):
     items = []
-    for provisioningResource, recordType, recordResource, record in self._allRecords():
+    for _ignore_provisioningResource, _ignore_recordType, recordResource, record in self._allRecords():
         if record.enabled:
-            items.append(( davxml.HRef().fromString(recordResource.principalURL()), davxml.Read()  , True ))
-            items.append(( davxml.HRef().fromString(recordResource.principalURL()), davxml.Write() , False ))
-    items.append(( davxml.Unauthenticated() , davxml.Read()  , False ))
-    items.append(( davxml.Unauthenticated() , davxml.Write() , False ))
-            
+            items.append((davxml.HRef().fromString(recordResource.principalURL()), davxml.Read()  , True))
+            items.append((davxml.HRef().fromString(recordResource.principalURL()), davxml.Write() , False))
+    items.append((davxml.Unauthenticated() , davxml.Read()  , False))
+    items.append((davxml.Unauthenticated() , davxml.Write() , False))
+
     for principal, privilege, allowed in items:
         yield resource, url, principal, privilege, allowed

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/resource.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/resource.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -1090,7 +1090,7 @@
 
 
     @inlineCallbacks
-    def movedCalendar(self, request, defaultCalendarType, destination, destination_uri):
+    def movedCalendar(self, request, destination, destination_uri):
         """
         Calendar has been moved. Need to do some extra clean-up.
         """
@@ -1099,17 +1099,11 @@
         principal = (yield self.resourceOwnerPrincipal(request))
         inboxURL = principal.scheduleInboxURL()
         if inboxURL:
-            (_ignore_scheme, _ignore_host, destination_path, _ignore_query, _ignore_fragment) = urlsplit(normalizeURL(destination_uri))
-
             inbox = (yield request.locateResource(inboxURL))
             inbox.processFreeBusyCalendar(request.path, False)
             inbox.processFreeBusyCalendar(destination_uri, destination.isCalendarOpaque())
 
-            # Adjust the default calendar setting if necessary
-            if defaultCalendarType is not None:
-                yield inbox.writeProperty(defaultCalendarType(element.HRef(destination_path)), request)
 
-
     def isCalendarOpaque(self):
 
         assert self.isCalendarCollection()
@@ -1121,22 +1115,13 @@
             return False
 
 
-    @inlineCallbacks
     def isDefaultCalendar(self, request):
 
         assert self.isCalendarCollection()
 
-        # Not allowed to delete the default calendar
-        principal = (yield self.resourceOwnerPrincipal(request))
-        inboxURL = principal.scheduleInboxURL()
-        if inboxURL:
-            inbox = (yield request.locateResource(inboxURL))
-            result = (yield inbox.isDefaultCalendar(request, self))
-            returnValue(result)
+        return self._newStoreParentHome.isDefaultCalendar(self._newStoreObject)
 
-        returnValue(None)
 
-
     @inlineCallbacks
     def iCalendarForUser(self, request):
 
@@ -2350,8 +2335,8 @@
                                 )
                             )
 
-
                         returnValue(customxml.PubSubPushTransportsProperty(*children))
+
             returnValue(None)
 
         elif qname == (customxml.calendarserver_namespace, "pushkey"):
@@ -2546,7 +2531,7 @@
         from twistedcaldav.storebridge import StoreScheduleInboxResource
         self._provisionedChildren["inbox"] = StoreScheduleInboxResource.maybeCreateInbox
 
-        from twistedcaldav.scheduling.caldav.resource import ScheduleOutboxResource
+        from twistedcaldav.scheduling_store.caldav.resource import ScheduleOutboxResource
         self._provisionedChildren["outbox"] = ScheduleOutboxResource
 
         if config.EnableDropBox and not config.EnableManagedAttachments:

Added: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/__init__.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/__init__.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -0,0 +1,15 @@
+##
+# Copyright (c) 2005-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.
+##

Added: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/__init__.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/__init__.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -0,0 +1,15 @@
+##
+# Copyright (c) 2005-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.
+##

Added: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/resource.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/resource.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -0,0 +1,489 @@
+# -*- test-case-name: twistedcaldav.directory.test.test_calendar -*-
+##
+# Copyright (c) 2005-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 txdav.caldav.icalendarstore import InvalidDefaultCalendar
+from twisted.python.failure import Failure
+
+"""
+CalDAV scheduling resources.
+"""
+
+__all__ = [
+    "ScheduleInboxResource",
+    "ScheduleOutboxResource",
+    "deliverSchedulePrivilegeSet",
+]
+
+
+from twistedcaldav.config import config
+# _schedulePrivilegeSet implicitly depends on config being initialized. The
+# following line is wrong because _schedulePrivilegeSet won't actually use the
+# config file, it will pick up stdconfig whenever it is imported, so this works
+# around that for now.
+__import__("twistedcaldav.stdconfig") # FIXME
+
+from twext.web2 import responsecode
+from twext.web2.dav.http import ErrorResponse, MultiStatusResponse
+from twext.web2.dav.resource import davPrivilegeSet
+from twext.web2.dav.util import joinURL, normalizeURL
+from twext.web2.http import HTTPError
+
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+
+from twistedcaldav import caldavxml, customxml
+from twistedcaldav.caldavxml import caldav_namespace, Opaque, \
+    CalendarFreeBusySet, ScheduleCalendarTransp
+from twistedcaldav.customxml import calendarserver_namespace
+from twistedcaldav.ical import allowedComponents, Component
+from twistedcaldav.resource import CalDAVResource
+from twistedcaldav.resource import isCalendarCollectionResource
+
+from txdav.base.propertystore.base import PropertyName
+from txdav.caldav.datastore.scheduling.caldav.scheduler import CalDAVScheduler
+from txdav.xml import element as davxml
+from txdav.xml.rfc2518 import HRef
+
+def _schedulePrivilegeSet(deliver):
+    edited = False
+
+    top_supported_privileges = []
+
+    for supported_privilege in davPrivilegeSet.childrenOfType(davxml.SupportedPrivilege):
+        all_privilege = supported_privilege.childOfType(davxml.Privilege)
+        if isinstance(all_privilege.children[0], davxml.All):
+            all_description = supported_privilege.childOfType(davxml.Description)
+            all_supported_privileges = list(supported_privilege.childrenOfType(davxml.SupportedPrivilege))
+            all_supported_privileges.append(
+                davxml.SupportedPrivilege(
+                    davxml.Privilege(caldavxml.ScheduleDeliver() if deliver else caldavxml.ScheduleSend()),
+                    davxml.Description("schedule privileges for current principal", **{"xml:lang": "en"}),
+                ),
+            )
+            if config.Scheduling.CalDAV.OldDraftCompatibility:
+                all_supported_privileges.append(
+                    davxml.SupportedPrivilege(
+                        davxml.Privilege(caldavxml.Schedule()),
+                        davxml.Description("old-style schedule privileges for current principal", **{"xml:lang": "en"}),
+                    ),
+                )
+            top_supported_privileges.append(
+                davxml.SupportedPrivilege(all_privilege, all_description, *all_supported_privileges)
+            )
+            edited = True
+        else:
+            top_supported_privileges.append(supported_privilege)
+
+    assert edited, "Structure of davPrivilegeSet changed in a way that I don't know how to extend for schedulePrivilegeSet"
+
+    return davxml.SupportedPrivilegeSet(*top_supported_privileges)
+
+deliverSchedulePrivilegeSet = _schedulePrivilegeSet(True)
+sendSchedulePrivilegeSet = _schedulePrivilegeSet(False)
+
+class CalendarSchedulingCollectionResource (CalDAVResource):
+    """
+    CalDAV principal resource.
+
+    Extends L{DAVResource} to provide CalDAV scheduling collection
+    functionality.
+    """
+    def __init__(self, parent):
+        """
+        @param parent: the parent resource of this one.
+        """
+        assert parent is not None
+
+        super(CalendarSchedulingCollectionResource, self).__init__(principalCollections=parent.principalCollections())
+
+        self.parent = parent
+
+
+    def isCollection(self):
+        return True
+
+
+    def isCalendarCollection(self):
+        return False
+
+
+    def isPseudoCalendarCollection(self):
+        return True
+
+
+    def supportedReports(self):
+        result = super(CalDAVResource, self).supportedReports()
+        result.append(davxml.Report(caldavxml.CalendarQuery(),))
+        result.append(davxml.Report(caldavxml.CalendarMultiGet(),))
+        # free-busy report not allowed
+        if config.EnableSyncReport:
+            # Only allowed on calendar/inbox/addressbook collections
+            result.append(davxml.Report(davxml.SyncCollection(),))
+        return result
+
+
+
+class ScheduleInboxResource (CalendarSchedulingCollectionResource):
+    """
+    CalDAV schedule Inbox resource.
+
+    Extends L{DAVResource} to provide CalDAV functionality.
+    """
+
+    def liveProperties(self):
+
+        return super(ScheduleInboxResource, self).liveProperties() + (
+            caldavxml.CalendarFreeBusySet.qname(),
+            caldavxml.ScheduleDefaultCalendarURL.qname(),
+            customxml.ScheduleDefaultTasksURL.qname(),
+        )
+
+
+    def resourceType(self):
+        return davxml.ResourceType.scheduleInbox
+
+
+    @inlineCallbacks
+    def readProperty(self, property, request):
+        if type(property) is tuple:
+            qname = property
+        else:
+            qname = property.qname()
+
+        if qname == caldavxml.CalendarFreeBusySet.qname():
+            # Always return at least an empty list
+            if not self.hasDeadProperty(property):
+                top = self.parent.url()
+                values = []
+                for cal in (yield self.parent._newStoreHome.calendars()):
+                    prop = cal.properties().get(PropertyName.fromString(ScheduleCalendarTransp.sname()))
+                    if prop == ScheduleCalendarTransp(Opaque()):
+                        values.append(HRef(joinURL(top, cal.name())))
+                returnValue(CalendarFreeBusySet(*values))
+        elif qname in (caldavxml.ScheduleDefaultCalendarURL.qname(), customxml.ScheduleDefaultTasksURL.qname()):
+            result = (yield self.readDefaultCalendarProperty(request, qname))
+            returnValue(result)
+
+        result = (yield super(ScheduleInboxResource, self).readProperty(property, request))
+        returnValue(result)
+
+
+    @inlineCallbacks
+    def writeProperty(self, property, request):
+        assert isinstance(property, davxml.WebDAVElement)
+
+        # Strictly speaking CS:calendar-availability is a live property in the sense that the
+        # server enforces what can be stored, however it need not actually
+        # exist so we cannot list it in liveProperties on this resource, since its
+        # its presence there means that hasProperty will always return True for it.
+        if property.qname() == customxml.CalendarAvailability.qname():
+            if not property.valid():
+                raise HTTPError(ErrorResponse(
+                    responsecode.CONFLICT,
+                    (caldav_namespace, "valid-calendar-data"),
+                    description="Invalid property"
+                ))
+
+        elif property.qname() == caldavxml.CalendarFreeBusySet.qname():
+            # Verify that the calendars added in the PROPPATCH are valid. We do not check
+            # whether existing items in the property are still valid - only new ones.
+            property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children]
+            new_calendars = set([str(href) for href in property.children])
+            if not self.hasDeadProperty(property):
+                old_calendars = set()
+            else:
+                old_calendars = set([normalizeURL(str(href)) for href in self.readDeadProperty(property).children])
+            added_calendars = new_calendars.difference(old_calendars)
+            for href in added_calendars:
+                cal = (yield request.locateResource(str(href)))
+                if cal is None or not cal.exists() or not isCalendarCollectionResource(cal):
+                    # Validate that href's point to a valid calendar.
+                    raise HTTPError(ErrorResponse(
+                        responsecode.CONFLICT,
+                        (caldav_namespace, "valid-calendar-url"),
+                        "Invalid URI",
+                    ))
+            for href in tuple(new_calendars):
+                cal = (yield request.locateResource(str(href)))
+                if cal is None or not cal.exists() or not isCalendarCollectionResource(cal):
+                    new_calendars.remove(href)
+            property.children = [davxml.HRef(href) for href in new_calendars]
+
+        elif property.qname() in (caldavxml.ScheduleDefaultCalendarURL.qname(), customxml.ScheduleDefaultTasksURL.qname()):
+            yield self.writeDefaultCalendarProperty(request, property)
+            returnValue(None)
+
+        yield super(ScheduleInboxResource, self).writeProperty(property, request)
+
+
+    def processFreeBusyCalendar(self, uri, addit):
+        uri = normalizeURL(uri)
+
+        if not self.hasDeadProperty(caldavxml.CalendarFreeBusySet.qname()):
+            fbset = set()
+        else:
+            fbset = set([normalizeURL(str(href)) for href in self.readDeadProperty(caldavxml.CalendarFreeBusySet.qname()).children])
+        if addit:
+            if uri not in fbset:
+                fbset.add(uri)
+                self.writeDeadProperty(caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in fbset]))
+        else:
+            if uri in fbset:
+                fbset.remove(uri)
+                self.writeDeadProperty(caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in fbset]))
+
+
+    @inlineCallbacks
+    def readDefaultCalendarProperty(self, request, qname):
+        """
+        Read either the default VEVENT or VTODO calendar property. Try to pick one if not present.
+        """
+
+        tasks = qname == customxml.ScheduleDefaultTasksURL.qname()
+        componentType = "VTODO" if tasks else "VEVENT"
+        prop_to_set = customxml.ScheduleDefaultTasksURL if tasks else caldavxml.ScheduleDefaultCalendarURL
+
+        # This property now comes direct from the calendar home new store object
+        default = (yield self.parent._newStoreHome.defaultCalendar(componentType))
+        defaultURL = joinURL(self.parent.url(), default.name())
+        returnValue(prop_to_set(davxml.HRef(defaultURL)))
+
+
+    @inlineCallbacks
+    def writeDefaultCalendarProperty(self, request, property):
+        """
+        Write either the default VEVENT or VTODO calendar property, validating and canonicalizing the value
+        """
+        tasks = property.qname() == customxml.ScheduleDefaultTasksURL
+        error_element = (calendarserver_namespace, "valid-schedule-default-tasks-URL") if tasks else (caldav_namespace, "valid-schedule-default-calendar-URL")
+
+        # Verify that the calendar added in the PROPPATCH is valid.
+        property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children]
+        new_calendar = [str(href) for href in property.children]
+        cal = None
+        if len(new_calendar) == 1:
+            cal = (yield request.locateResource(str(new_calendar[0])))
+        else:
+            raise HTTPError(ErrorResponse(
+                responsecode.BAD_REQUEST,
+                error_element,
+                "Invalid HRef in property",
+            ))
+
+        try:
+            # Now set it on the new store object
+            yield self.parent._newStoreHome.setDefaultCalendar(cal._newStoreObject, tasks)
+        except InvalidDefaultCalendar as e:
+            raise HTTPError(ErrorResponse(
+                responsecode.CONFLICT,
+                error_element,
+                str(e),
+            ))
+
+
+    @inlineCallbacks
+    def defaultCalendar(self, request, componentType):
+        """
+        Find the default calendar for the supplied iCalendar component type. If one does
+        not exist, automatically provision it.
+        """
+
+        # This property now comes direct from the calendar home new store object
+        default = (yield self.parent._newStoreHome.defaultCalendar(componentType))
+
+        # Need L{DAVResource} object to return not new store object
+        default = (yield request.locateResource(joinURL(self.parent.url(), default.name())))
+
+        returnValue(default)
+
+
+    ##
+    # ACL
+    ##
+
+    def supportedPrivileges(self, request):
+        return succeed(deliverSchedulePrivilegeSet)
+
+
+    def defaultAccessControlList(self):
+
+        privs = (
+            davxml.Privilege(caldavxml.ScheduleDeliver()),
+        )
+        if config.Scheduling.CalDAV.OldDraftCompatibility:
+            privs += (davxml.Privilege(caldavxml.Schedule()),)
+
+        return davxml.ACL(
+            # CalDAV:schedule-deliver for any authenticated user
+            davxml.ACE(
+                davxml.Principal(davxml.Authenticated()),
+                davxml.Grant(*privs),
+            ),
+        )
+
+
+
+class ScheduleOutboxResource (CalendarSchedulingCollectionResource):
+    """
+    CalDAV schedule Outbox resource.
+
+    Extends L{DAVResource} to provide CalDAV functionality.
+    """
+
+    def resourceType(self):
+        return davxml.ResourceType.scheduleOutbox
+
+
+    def getSupportedComponentSet(self):
+        return caldavxml.SupportedCalendarComponentSet(
+            *[caldavxml.CalendarComponent(name=item) for item in allowedComponents]
+        )
+
+
+    @inlineCallbacks
+    def http_POST(self, request):
+        """
+        The CalDAV POST method.
+
+        This uses a generator function yielding either L{waitForDeferred} objects or L{Response} objects.
+        This allows for code that follows a 'linear' execution pattern rather than having to use nested
+        L{Deferred} callbacks. The logic is easier to follow this way plus we don't run into deep nesting
+        issues which the other approach would have with large numbers of recipients.
+        """
+        # Check authentication and access controls
+        yield self.authorize(request, (caldavxml.ScheduleSend(),))
+
+        calendar = (yield self.loadCalendarFromRequest(request))
+        originator = (yield self.loadOriginatorFromRequestDetails(request))
+        recipients = self.loadRecipientsFromCalendarData()
+
+        # This is a local CALDAV scheduling operation.
+        scheduler = CalDAVScheduler(self._associatedTransaction, self.parent._newStoreHome.uid())
+
+        # Do the POST processing treating
+        result = (yield scheduler.doSchedulingViaPOST(originator, recipients, calendar))
+        returnValue(result.response())
+
+
+    @inlineCallbacks
+    def loadCalendarFromRequest(self, request):
+        # Must be content-type text/calendar
+        contentType = request.headers.getHeader("content-type")
+        if contentType is not None and (contentType.mediaType, contentType.mediaSubtype) != ("text", "calendar"):
+            self.log_error("MIME type %s not allowed in calendar collection" % (contentType,))
+            raise HTTPError(ErrorResponse(
+                responsecode.FORBIDDEN,
+                (caldav_namespace, "supported-calendar-data"),
+                "Data is not calendar data",
+            ))
+
+        # Parse the calendar object from the HTTP request stream
+        try:
+            calendar = (yield Component.fromIStream(request.stream))
+        except:
+            # FIXME: Bare except
+            self.log_error("Error while handling POST: %s" % (Failure(),))
+            raise HTTPError(ErrorResponse(
+                responsecode.FORBIDDEN,
+                (caldav_namespace, "valid-calendar-data"),
+                description="Can't parse calendar data"
+            ))
+
+        returnValue(calendar)
+
+
+    @inlineCallbacks
+    def loadOriginatorFromRequestDetails(self, request):
+        # Get the originator who is the authenticated user
+        originatorPrincipal = None
+        originator = ""
+        authz_principal = self.currentPrincipal(request).children[0]
+        if isinstance(authz_principal, davxml.HRef):
+            originatorPrincipalURL = str(authz_principal)
+            if originatorPrincipalURL:
+                originatorPrincipal = (yield request.locateResource(originatorPrincipalURL))
+                if originatorPrincipal:
+                    # Pick the canonical CUA:
+                    originator = originatorPrincipal.canonicalCalendarUserAddress()
+
+        if not originator:
+            self.log_err("%s request must have Originator" % (self.method,))
+            raise HTTPError(ErrorResponse(
+                responsecode.FORBIDDEN,
+                (caldav_namespace, "originator-specified"),
+                "Missing originator",
+            ))
+        else:
+            returnValue(originator)
+
+
+    def loadRecipientsFromCalendarData(self, calendar):
+
+        # Get the ATTENDEEs
+        attendees = list()
+        unique_set = set()
+        for attendee, _ignore in calendar.getAttendeesByInstance():
+            if attendee not in unique_set:
+                attendees.append(attendee)
+                unique_set.add(attendee)
+
+        if not attendees:
+            self.log_err("POST request must have at least one ATTENDEE")
+            raise HTTPError(ErrorResponse(
+                responsecode.FORBIDDEN,
+                (caldav_namespace, "recipient-specified"),
+                "Must have recipients",
+            ))
+        else:
+            return(list(attendees))
+
+
+    ##
+    # ACL
+    ##
+
+    def supportedPrivileges(self, request):
+        return succeed(sendSchedulePrivilegeSet)
+
+
+    def defaultAccessControlList(self):
+        if config.EnableProxyPrincipals:
+            myPrincipal = self.parent.principalForRecord()
+
+            privs = (
+                davxml.Privilege(caldavxml.ScheduleSend()),
+            )
+            if config.Scheduling.CalDAV.OldDraftCompatibility:
+                privs += (davxml.Privilege(caldavxml.Schedule()),)
+
+            return davxml.ACL(
+                # CalDAV:schedule for associated write proxies
+                davxml.ACE(
+                    davxml.Principal(davxml.HRef(joinURL(myPrincipal.principalURL(), "calendar-proxy-write"))),
+                    davxml.Grant(*privs),
+                    davxml.Protected(),
+                ),
+            )
+        else:
+            return super(ScheduleOutboxResource, self).defaultAccessControlList()
+
+
+    def report_urn_ietf_params_xml_ns_caldav_calendar_query(self, request, calendar_query):
+        return succeed(MultiStatusResponse(()))
+
+
+    def report_urn_ietf_params_xml_ns_caldav_calendar_multiget(self, request, multiget):
+        responses = [davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)) for href in multiget.resources]
+        return succeed(MultiStatusResponse((responses)))

Added: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/test/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/test/__init__.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/test/__init__.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -0,0 +1,15 @@
+##
+# Copyright (c) 2005-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.
+##

Added: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/test/test_resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/test/test_resource.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/scheduling_store/caldav/test/test_resource.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -0,0 +1,344 @@
+##
+# Copyright (c) 2005-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 twext.web2 import responsecode, http_headers
+from twext.web2.dav.util import davXMLFromStream
+from twext.web2.iweb import IResponse
+from twext.web2.stream import MemoryStream
+from twext.web2.test.test_server import SimpleRequest
+
+from twisted.internet.defer import inlineCallbacks
+
+from twistedcaldav import caldavxml, customxml
+from twistedcaldav.test.util import HomeTestCase, StoreTestCase, \
+    SimpleStoreRequest
+
+from txdav.xml import element as davxml
+
+class Properties (HomeTestCase):
+    """
+    CalDAV properties
+    """
+    def test_free_busy_set_prop(self):
+        """
+        Test for PROPFIND on Inbox with missing calendar-free-busy-set property.
+        """
+
+        inbox_uri = "/inbox/"
+
+        def propfind_cb(response):
+            response = IResponse(response)
+
+            if response.code != responsecode.MULTI_STATUS:
+                self.fail("Incorrect response to PROPFIND: %s" % (response.code,))
+
+            def got_xml(doc):
+                if not isinstance(doc.root_element, davxml.MultiStatus):
+                    self.fail("PROPFIND response XML root element is not multistatus: %r" % (doc.root_element,))
+
+                response = doc.root_element.childOfType(davxml.Response)
+                href = response.childOfType(davxml.HRef)
+                self.failUnless(str(href) == inbox_uri)
+
+                for propstat in response.childrenOfType(davxml.PropertyStatus):
+                    status = propstat.childOfType(davxml.Status)
+                    if status.code != responsecode.OK:
+                        self.fail("Unable to read requested properties (%s): %r"
+                                  % (status, propstat.childOfType(davxml.PropertyContainer).toxml()))
+
+                container = propstat.childOfType(davxml.PropertyContainer)
+
+                #
+                # Check CalDAV:calendar-free-busy-set
+                #
+
+                free_busy_set = container.childOfType(caldavxml.CalendarFreeBusySet)
+                if not free_busy_set:
+                    self.fail("Expected CalDAV:calendar-free-busy-set element; but got none.")
+
+                if not free_busy_set.children:
+                    self.fail("Expected non-empty CalDAV:calendar-free-busy-set element.")
+
+            return davXMLFromStream(response.stream).addCallback(got_xml)
+
+        query = davxml.PropertyFind(
+                    davxml.PropertyContainer(
+                        caldavxml.CalendarFreeBusySet(),
+                    ),
+                )
+
+        request = SimpleRequest(
+            self.site,
+            "PROPFIND",
+            inbox_uri,
+            headers=http_headers.Headers({"Depth": "0"}),
+        )
+        request.stream = MemoryStream(query.toxml())
+        return self.send(request, propfind_cb)
+
+
+    @inlineCallbacks
+    def test_free_busy_set_remove_broken(self):
+        """
+        ???
+        """
+
+        request = SimpleRequest(self.site, "GET", "/inbox/")
+        inbox = yield request.locateResource("/inbox/")
+        self.assertTrue(inbox.hasDeadProperty(caldavxml.CalendarFreeBusySet))
+        oldfbset = set(("/calendar",))
+        oldset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in oldfbset])
+
+        newfbset = set()
+        newfbset.update(oldfbset)
+        newfbset.add("/calendar-broken")
+        newset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in newfbset])
+
+        inbox.writeDeadProperty(newset)
+        changedset = inbox.readDeadProperty(caldavxml.CalendarFreeBusySet)
+        self.assertEqual(tuple(changedset.children), tuple(newset.children))
+
+        yield inbox.writeProperty(newset, request)
+
+        changedset = inbox.readDeadProperty(caldavxml.CalendarFreeBusySet)
+        self.assertEqual(tuple(changedset.children), tuple(oldset.children))
+
+
+    @inlineCallbacks
+    def test_free_busy_set_strip_slash(self):
+        """
+        ???
+        """
+
+        request = SimpleRequest(self.site, "GET", "/inbox/")
+        inbox = yield request.locateResource("/inbox/")
+        self.assertTrue(inbox.hasDeadProperty(caldavxml.CalendarFreeBusySet))
+
+        oldfbset = set(("/calendar/",))
+        oldset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in oldfbset])
+        inbox.writeDeadProperty(oldset)
+
+        writefbset = set(("/calendar/",))
+        writeset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in writefbset])
+        yield inbox.writeProperty(writeset, request)
+
+        correctfbset = set(("/calendar",))
+        correctset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in correctfbset])
+        changedset = inbox.readDeadProperty(caldavxml.CalendarFreeBusySet)
+        self.assertEqual(tuple(changedset.children), tuple(correctset.children))
+
+
+    @inlineCallbacks
+    def test_free_busy_set_strip_slash_remove(self):
+        """
+        ???
+        """
+
+        request = SimpleRequest(self.site, "GET", "/inbox/")
+        inbox = yield request.locateResource("/inbox/")
+        self.assertTrue(inbox.hasDeadProperty(caldavxml.CalendarFreeBusySet))
+
+        oldfbset = set(("/calendar/", "/broken/"))
+        oldset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in oldfbset])
+        inbox.writeDeadProperty(oldset)
+
+        writefbset = set(("/calendar/", "/broken/"))
+        writeset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in writefbset])
+        yield inbox.writeProperty(writeset, request)
+
+        correctfbset = set(("/calendar",))
+        correctset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in correctfbset])
+        changedset = inbox.readDeadProperty(caldavxml.CalendarFreeBusySet)
+        self.assertEqual(tuple(changedset.children), tuple(correctset.children))
+
+
+
+class DefaultCalendar (StoreTestCase):
+
+    @inlineCallbacks
+    def test_pick_default_vevent_calendar(self):
+        """
+        Test that pickNewDefaultCalendar will choose the correct calendar.
+        """
+
+        request = SimpleStoreRequest(self, "GET", "/calendars/users/wsanchez/")
+        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+
+        # default property initially present
+        prop = yield inbox.readProperty(caldavxml.ScheduleDefaultCalendarURL, request)
+        self.assertEqual(str(prop.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar")
+
+        yield self.abort()
+
+
+    @inlineCallbacks
+    def test_pick_default_vtodo_calendar(self):
+        """
+        Test that pickNewDefaultCalendar will choose the correct tasks calendar.
+        """
+
+        request = SimpleStoreRequest(self, "GET", "/calendars/users/wsanchez/")
+        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+
+        default = yield inbox.readProperty(customxml.ScheduleDefaultTasksURL, request)
+        self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/tasks")
+
+        yield self.abort()
+
+
+    @inlineCallbacks
+    def test_missing_default_vevent_calendar(self):
+        """
+        Test that pickNewDefaultCalendar will create a missing default calendar.
+        """
+
+        request = SimpleStoreRequest(self, "GET", "/calendars/users/wsanchez/")
+        home = yield request.locateResource("/calendars/users/wsanchez/")
+        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+
+        # default property initially not present
+        default = yield inbox.readProperty(caldavxml.ScheduleDefaultCalendarURL, request)
+        self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar")
+
+        # Forcibly remove the one we need
+        yield home._newStoreHome.removeChildWithName("calendar")
+        names = [calendarName for calendarName in (yield home._newStoreHome.listCalendars())]
+        self.assertTrue("calendar" not in names)
+
+        default = yield inbox.readProperty(caldavxml.ScheduleDefaultCalendarURL, request)
+        self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar")
+
+        yield self.abort()
+
+
+    @inlineCallbacks
+    def test_missing_default_vtodo_calendar(self):
+        """
+        Test that pickNewDefaultCalendar will create a missing default tasks calendar.
+        """
+
+        request = SimpleStoreRequest(self, "GET", "/calendars/users/wsanchez/")
+        home = yield request.locateResource("/calendars/users/wsanchez/")
+        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+
+        # default property present
+        default = yield inbox.readProperty(customxml.ScheduleDefaultTasksURL, request)
+        self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/tasks")
+
+        # Forcibly remove the one we need
+        yield home._newStoreHome.removeChildWithName("tasks")
+        names = [calendarName for calendarName in (yield home._newStoreHome.listCalendars())]
+        self.assertTrue("tasks" not in names)
+
+        default = yield inbox.readProperty(customxml.ScheduleDefaultTasksURL, request)
+        self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/tasks")
+
+        yield self.abort()
+
+
+    @inlineCallbacks
+    def test_pick_default_other(self):
+        """
+        Make calendar
+        """
+
+        request = SimpleStoreRequest(self, "GET", "/calendars/users/wsanchez/")
+        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+
+        # default property present
+        default = yield inbox.readProperty(caldavxml.ScheduleDefaultCalendarURL, request)
+        self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar")
+
+        # Create a new default calendar
+        newcalendar = yield request.locateResource("/calendars/users/wsanchez/newcalendar")
+        yield newcalendar.createCalendarCollection()
+        yield inbox.writeProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef("/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")), request)
+
+        # Delete the normal calendar
+        calendar = yield request.locateResource("/calendars/users/wsanchez/calendar")
+        yield calendar.storeRemove(request)
+        yield self.commit()
+
+        request = SimpleStoreRequest(self, "GET", "/calendars/users/wsanchez/")
+        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+
+        default = yield inbox.readProperty(caldavxml.ScheduleDefaultCalendarURL, request)
+        self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")
+
+        yield self.abort()
+
+
+    @inlineCallbacks
+    def test_set_default_vevent_other(self):
+        """
+        Test that the default URL can be set to another VEVENT calendar
+        """
+
+        request = SimpleStoreRequest(self, "GET", "/calendars/users/wsanchez/")
+        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+
+        # default property is present
+        default = yield inbox.readProperty(caldavxml.ScheduleDefaultCalendarURL, request)
+        self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar")
+
+        # Create a new default calendar
+        newcalendar = yield request.locateResource("/calendars/users/wsanchez/newcalendar")
+        yield newcalendar.createCalendarCollection()
+        yield newcalendar.setSupportedComponents(("VEVENT",))
+        yield self.commit()
+
+        request = SimpleStoreRequest(self, "GET", "/calendars/users/wsanchez/")
+        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+        yield inbox.writeProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef("/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")), request)
+
+        default = yield inbox.readProperty(caldavxml.ScheduleDefaultCalendarURL, request)
+        self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")
+
+        yield self.commit()
+
+
+    @inlineCallbacks
+    def test_is_default_calendar(self):
+        """
+        Test .isDefaultCalendar() returns the proper class or None.
+        """
+
+        # Create a new non-default calendar
+        request = SimpleStoreRequest(self, "GET", "/calendars/users/wsanchez/")
+        newcalendar = yield request.locateResource("/calendars/users/wsanchez/newcalendar")
+        yield newcalendar.createCalendarCollection()
+        yield newcalendar.setSupportedComponents(("VEVENT",))
+        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+        yield inbox.defaultCalendar(request, "VEVENT")
+        yield inbox.defaultCalendar(request, "VTODO")
+        yield self.commit()
+
+        request = SimpleStoreRequest(self, "GET", "/calendars/users/wsanchez/")
+        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
+        calendar = yield request.locateResource("/calendars/users/wsanchez/calendar")
+        newcalendar = yield request.locateResource("/calendars/users/wsanchez/newcalendar")
+        tasks = yield request.locateResource("/calendars/users/wsanchez/tasks")
+
+        result = yield calendar.isDefaultCalendar(request)
+        self.assertTrue(result)
+
+        result = yield newcalendar.isDefaultCalendar(request)
+        self.assertFalse(result)
+
+        result = yield tasks.isDefaultCalendar(request)
+        self.assertTrue(result)
+
+        yield self.commit()

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -48,11 +48,10 @@
 from twistedcaldav.ical import Component as VCalendar, Property as VProperty, \
     InvalidICalendarDataError, iCalendarProductID, allowedComponents, Component
 from twistedcaldav.memcachelock import MemcacheLockTimeoutError
-from twistedcaldav.method.put_addressbook_common import StoreAddressObjectResource
 from twistedcaldav.notifications import NotificationCollectionResource, NotificationResource
 from twistedcaldav.resource import CalDAVResource, GlobalAddressBookResource, \
     DefaultAlarmPropertyMixin
-from twistedcaldav.scheduling.caldav.resource import ScheduleInboxResource
+from twistedcaldav.scheduling_store.caldav.resource import ScheduleInboxResource
 from twistedcaldav.scheduling.implicit import ImplicitScheduler
 from twistedcaldav.vcard import Component as VCard, InvalidVCardDataError
 
@@ -1271,14 +1270,11 @@
         Moving a calendar collection is allowed for the purposes of changing
         that calendar's name.
         """
-        defaultCalendarType = (yield self.isDefaultCalendar(request))
-
         result = (yield super(CalendarCollectionResource, self).http_MOVE(request))
         if result == NO_CONTENT:
             destinationURI = urlsplit(request.headers.getHeader("destination"))[2]
             destination = yield request.locateResource(destinationURI)
-            yield self.movedCalendar(request, defaultCalendarType,
-                               destination, destinationURI)
+            yield self.movedCalendar(request, destination, destinationURI)
         returnValue(result)
 
 

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_calendarquery.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_calendarquery.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_calendarquery.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -18,25 +18,24 @@
 
 from twisted.trial.unittest import SkipTest
 
-from twext.python.filepath import CachingFilePath as FilePath
-
 from twext.web2 import responsecode
 from twext.web2.iweb import IResponse
 from twext.web2.stream import MemoryStream
 from txdav.xml import element as davxml
 from twext.web2.dav.util import davXMLFromStream
-from twext.web2.test.test_server import SimpleRequest
 
 from twistedcaldav import caldavxml
 from twistedcaldav import ical
 
 from twistedcaldav.query import calendarqueryfilter
 from twistedcaldav.config import config
-from twistedcaldav.test.util import HomeTestCase
+from twistedcaldav.test.util import StoreTestCase, SimpleStoreRequest
 from twisted.internet.defer import inlineCallbacks, returnValue
-from txdav.common.datastore.test.util import buildStore, StubNotifierFactory
 
 from pycalendar.datetime import PyCalendarDateTime
+from twistedcaldav.ical import Component
+from txdav.caldav.icalendarstore import ComponentUpdateState
+from twistedcaldav.directory.directory import DirectoryService
 
 
 @inlineCallbacks
@@ -61,21 +60,35 @@
         count += 1
         if child.basename().split(".")[-1] != "ics":
             continue
-        request = SimpleRequest(testCase.site, "PUT",
-                                uri + "/" + child.basename())
+        request = SimpleStoreRequest(testCase, "PUT", uri + "/" + child.basename())
         request.stream = MemoryStream(child.getContent())
         yield testCase.send(request)
     returnValue(count)
 
 
 
-class CalendarQuery (HomeTestCase):
+class CalendarQuery (StoreTestCase):
     """
     calendar-query REPORT
     """
     data_dir = os.path.join(os.path.dirname(__file__), "data")
     holidays_dir = os.path.join(data_dir, "Holidays")
 
+    @inlineCallbacks
+    def populate(self):
+        """
+        Put the contents of the Holidays directory into the store.
+        """
+        record = self.directory.recordWithShortName(DirectoryService.recordType_users, "wsanchez")
+        yield self.transactionUnderTest().calendarHomeWithUID(record.uid, create=True)
+        calendar = yield self.calendarUnderTest(name="calendar", home=record.uid)
+        for f in os.listdir(self.holidays_dir):
+            if f.endswith(".ics"):
+                component = Component.fromString(open(os.path.join(self.holidays_dir, f)).read())
+                yield calendar._createCalendarObjectWithNameInternal(f, component, internal_state=ComponentUpdateState.RAW)
+        yield self.commit()
+
+
     def test_calendar_query_time_range(self):
         """
         Partial retrieval of events by time range.
@@ -158,8 +171,9 @@
                                 self.fail("REPORT property %r returned calendar %s outside of request time range %r"
                                           % (property, property.calendar, query_timerange))
 
-        return self.calendar_query("/calendar_query_time_range/", query, got_xml)
+        return self.calendar_query(query, got_xml)
 
+
     def test_calendar_query_partial_recurring(self):
         """
         Partial retrieval of recurring events.
@@ -167,6 +181,7 @@
         """
         raise SkipTest("test unimplemented")
 
+
     def test_calendar_query_expanded_recurring(self):
         """
         Expanded retrieval of recurring events.
@@ -174,6 +189,7 @@
         """
         raise SkipTest("test unimplemented")
 
+
     def test_calendar_query_partial_freebusy(self):
         """
         Partial retrieval of stored free busy components.
@@ -181,6 +197,7 @@
         """
         raise SkipTest("test unimplemented")
 
+
     def test_calendar_query_todo_alarm(self):
         """
         Retrieval of to-dos by alarm time range.
@@ -188,6 +205,7 @@
         """
         raise SkipTest("test unimplemented")
 
+
     def test_calendar_query_by_uid(self):
         """
         Event by UID.
@@ -196,7 +214,6 @@
         uid = "C3189A88-1ED0-11D9-A5E0-000A958A3252"
 
         return self.simple_event_query(
-            "/calendar_query_uid/",
             caldavxml.PropertyFilter(
                 caldavxml.TextMatch.fromString(uid, False),
                 name="UID",
@@ -204,6 +221,7 @@
             [uid]
         )
 
+
     def test_calendar_query_partstat(self):
         """
         Retrieval of events by participation status.
@@ -211,6 +229,7 @@
         """
         raise SkipTest("test unimplemented")
 
+
     def test_calendar_query_all_events(self):
         """
         All events.
@@ -219,53 +238,47 @@
         uids = [r[0] for r in (os.path.splitext(f) for f in
                 os.listdir(self.holidays_dir)) if r[1] == ".ics"]
 
-        return self.simple_event_query("/calendar_query_events/", None, uids)
+        return self.simple_event_query(None, uids)
 
+
     def test_calendar_query_limited_with_data(self):
         """
         All events.
         (CalDAV-access-09, section 7.6.8)
         """
-        
-        oldValue = config.MaxQueryWithDataResults
-        config.MaxQueryWithDataResults = 1
+
+        self.patch(config, "MaxQueryWithDataResults", 1)
         def _restoreValueOK(f):
-            config.MaxQueryWithDataResults = oldValue
             self.fail("REPORT must fail with 403")
 
         def _restoreValueError(f):
-            config.MaxQueryWithDataResults = oldValue
             return None
 
         uids = [r[0] for r in (os.path.splitext(f) for f in os.listdir(self.holidays_dir)) if r[1] == ".ics"]
 
-        d = self.simple_event_query("/calendar_query_events/", None, uids)
+        d = self.simple_event_query(None, uids)
         d.addCallbacks(_restoreValueOK, _restoreValueError)
         return d
 
+
     def test_calendar_query_limited_without_data(self):
         """
         All events.
         (CalDAV-access-09, section 7.6.8)
         """
-        
-        oldValue = config.MaxQueryWithDataResults
-        config.MaxQueryWithDataResults = 1
-        def _restoreValueOK(f):
-            config.MaxQueryWithDataResults = oldValue
-            return None
 
+        self.patch(config, "MaxQueryWithDataResults", 1)
         def _restoreValueError(f):
-            config.MaxQueryWithDataResults = oldValue
             self.fail("REPORT must not fail with 403")
 
         uids = [r[0] for r in (os.path.splitext(f) for f in os.listdir(self.holidays_dir)) if r[1] == ".ics"]
 
-        d = self.simple_event_query("/calendar_query_events/", None, uids, withData=False)
-        d.addCallbacks(_restoreValueOK, _restoreValueError)
+        d = self.simple_event_query(None, uids, withData=False)
+        d.addErrback(_restoreValueError)
         return d
 
-    def simple_event_query(self, cal_uri, event_filter, uids, withData=True):
+
+    def simple_event_query(self, event_filter, uids, withData=True):
         props = (
             davxml.GETETag(),
         )
@@ -302,7 +315,8 @@
 
                     for property in properties:
                         qname = property.qname()
-                        if qname == (davxml.dav_namespace, "getetag"): continue
+                        if qname == (davxml.dav_namespace, "getetag"):
+                            continue
                         if qname != (caldavxml.caldav_namespace, "calendar-data"):
                             self.fail("Response included unexpected property %r" % (property,))
 
@@ -323,22 +337,13 @@
 
                         self.assertEqual(result_calendar, original_calendar)
 
-        return self.calendar_query(cal_uri, query, got_xml)
+        return self.calendar_query(query, got_xml)
 
 
     @inlineCallbacks
-    def calendar_query(self, calendar_uri, query, got_xml):
+    def calendar_query(self, query, got_xml):
 
-        response = yield self.send(SimpleRequest(self.site, "MKCALENDAR", calendar_uri))
-        response = IResponse(response)
-
-        if response.code != responsecode.CREATED:
-            self.fail("MKCALENDAR failed: %s" % (response.code,))
-
-        # Add holiday events to calendar
-        yield addEventsDir(self, FilePath(self.holidays_dir), calendar_uri)
-
-        request = SimpleRequest(self.site, "REPORT", calendar_uri)
+        request = SimpleStoreRequest(self, "REPORT", "/calendars/users/wsanchez/calendar/", authid="wsanchez")
         request.stream = MemoryStream(query.toxml())
         response = yield self.send(request)
 
@@ -350,17 +355,3 @@
         returnValue(
             (yield davXMLFromStream(response.stream).addCallback(got_xml))
         )
-
-
-class DatabaseQueryTests(CalendarQuery):
-
-    @inlineCallbacks
-    def setUp(self):
-        self.calendarStore = yield buildStore(self, StubNotifierFactory())
-        yield super(DatabaseQueryTests, self).setUp()
-
-
-    def createDataStore(self):
-        return self.calendarStore
-
-

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_collectioncontents.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_collectioncontents.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_collectioncontents.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -20,7 +20,6 @@
 from twext.web2.iweb import IResponse
 from twext.web2.stream import MemoryStream, FileStream
 from twext.web2.http_headers import MimeType
-from twext.web2.test.test_server import SimpleRequest
 
 from twistedcaldav.ical import Component
 from twistedcaldav.memcachelock import MemcacheLock
@@ -28,9 +27,10 @@
 from twistedcaldav.method.put_common import StoreCalendarObjectResource
 
 
-from twistedcaldav.test.util import HomeTestCase
+from twistedcaldav.test.util import StoreTestCase, SimpleStoreRequest
+from twext.web2.dav.util import joinURL
 
-class CollectionContents(HomeTestCase):
+class CollectionContents(StoreTestCase):
     """
     PUT request
     """
@@ -65,7 +65,7 @@
         """
         Make (regular) collection in calendar
         """
-        calendar_uri = "/collection_in_calendar/"
+        calendar_uri = "/calendars/users/wsanchez/collection_in_calendar/"
 
         def mkcalendar_cb(response):
             response = IResponse(response)
@@ -79,26 +79,28 @@
                 if response.code != responsecode.FORBIDDEN:
                     self.fail("Incorrect response to nested MKCOL: %s" % (response.code,))
 
-            nested_uri = "/".join([calendar_uri, "nested"])
+            nested_uri = joinURL(calendar_uri, "nested")
 
-            request = SimpleRequest(self.site, "MKCOL", nested_uri)
-            self.send(request, mkcol_cb)
+            request = SimpleStoreRequest(self, "MKCOL", nested_uri, authid="wsanchez")
+            return self.send(request, mkcol_cb)
 
-        request = SimpleRequest(self.site, "MKCALENDAR", calendar_uri)
+        request = SimpleStoreRequest(self, "MKCALENDAR", calendar_uri, authid="wsanchez")
         return self.send(request, mkcalendar_cb)
 
+
     def test_bogus_file(self):
         """
         Bogus file in calendar collection
         """
+
         # FIXME: Should FileStream be OK here?
+        # FIXME: Should FileStream be OK here?
         dst_file = file(__file__)
-        try:
-            stream = FileStream(dst_file)
-            return self._test_file_in_calendar("bogus file in calendar", (stream, responsecode.FORBIDDEN))
-        finally:
-            dst_file.close()
+        self.addCleanup(dst_file.close)
+        stream = FileStream(dst_file)
+        return self._test_file_in_calendar("bogus file in calendar", (stream, responsecode.FORBIDDEN))
 
+
     def openHolidays(self):
         """
         Open the 'Holidays.ics' calendar.
@@ -135,7 +137,8 @@
             if subcomponent.name() == "VEVENT":
                 subcalendar = Component("VCALENDAR")
                 subcalendar.addComponent(subcomponent)
-                for property in calendar.properties(): subcalendar.addProperty(property)
+                for property in calendar.properties():
+                    subcalendar.addProperty(property)
                 work.append((MemoryStream(str(subcalendar)), responsecode.CREATED))
 
         return self._test_file_in_calendar("single event in calendar", *work)
@@ -148,8 +151,10 @@
         stream = self.dataPath.child(
             "Holidays").child(
             "C318AA54-1ED0-11D9-A5E0-000A958A3252.ics").open()
-        try: calendar = str(Component.fromStream(stream))
-        finally: stream.close()
+        try:
+            calendar = str(Component.fromStream(stream))
+        finally:
+            stream.close()
 
         return self._test_file_in_calendar(
             "mutiple resources with the same UID",
@@ -164,7 +169,7 @@
         with the data from given stream and verifies that the response code from the
         PUT request matches the given response_code.
         """
-        calendar_uri = "/testing_calendar/"
+        calendar_uri = "/calendars/users/wsanchez/testing_calendar/"
 
 
         @inlineCallbacks
@@ -178,8 +183,8 @@
 
             for stream, response_code in work:
 
-                dst_uri = "/".join([calendar_uri, "dst%d.ics" % (c,)])
-                request = SimpleRequest(self.site, "PUT", dst_uri)
+                dst_uri = joinURL(calendar_uri, "dst%d.ics" % (c,))
+                request = SimpleStoreRequest(self, "PUT", dst_uri, authid="wsanchez")
                 request.headers.setHeader("if-none-match", "*")
                 request.headers.setHeader("content-type", MimeType("text", "calendar"))
                 request.stream = stream
@@ -191,7 +196,7 @@
 
                 c += 1
 
-        request = SimpleRequest(self.site, "MKCALENDAR", calendar_uri)
+        request = SimpleStoreRequest(self, "MKCALENDAR", calendar_uri, authid="wsanchez")
         return self.send(request, mkcalendar_cb)
 
 
@@ -199,7 +204,7 @@
         """
         Make (regular) collection in calendar
         """
-        calendar_uri = "/dot_file_in_calendar/"
+        calendar_uri = "/calendars/users/wsanchez/dot_file_in_calendar/"
 
         def mkcalendar_cb(response):
             response = IResponse(response)
@@ -217,16 +222,17 @@
                 "Holidays").child(
                 "C318AA54-1ED0-11D9-A5E0-000A958A3252.ics"
             ).open()
-            try: calendar = str(Component.fromStream(stream))
-            finally: stream.close()
+            try:
+                calendar = str(Component.fromStream(stream))
+            finally:
+                stream.close()
 
             event_uri = "/".join([calendar_uri, ".event.ics"])
 
-            request = SimpleRequest(self.site, "PUT", event_uri)
+            request = SimpleStoreRequest(self, "PUT", event_uri, authid="wsanchez")
             request.headers.setHeader("content-type", MimeType("text", "calendar"))
             request.stream = MemoryStream(calendar)
             return self.send(request, put_cb)
 
-        request = SimpleRequest(self.site, "MKCALENDAR", calendar_uri)
+        request = SimpleStoreRequest(self, "MKCALENDAR", calendar_uri, authid="wsanchez")
         return self.send(request, mkcalendar_cb)
-

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_multiget.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_multiget.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_multiget.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -13,23 +13,24 @@
 # limitations under the License.
 ##
 
-import os
-
-from twisted.internet.defer import inlineCallbacks, returnValue
 from twext.python.filepath import CachingFilePath as FilePath
 from twext.web2 import responsecode
+from twext.web2.dav.util import davXMLFromStream, joinURL
 from twext.web2.iweb import IResponse
 from twext.web2.stream import MemoryStream
-from txdav.xml import element as davxml
-from twext.web2.dav.util import davXMLFromStream
-from twext.web2.test.test_server import SimpleRequest
 
+from twisted.internet.defer import inlineCallbacks, returnValue
+
 from twistedcaldav import caldavxml
 from twistedcaldav import ical
-from twistedcaldav.test.util import HomeTestCase, todo
 from twistedcaldav.config import config
+from twistedcaldav.test.util import todo, StoreTestCase, SimpleStoreRequest
 
-class CalendarMultiget (HomeTestCase):
+from txdav.xml import element as davxml
+
+import os
+
+class CalendarMultiget (StoreTestCase):
     """
     calendar-multiget REPORT
     """
@@ -48,6 +49,7 @@
 
         return self.simple_event_multiget("/calendar_multiget_events/", okuids, baduids)
 
+
     def test_multiget_all_events(self):
         """
         All events.
@@ -59,6 +61,7 @@
 
         return self.simple_event_multiget("/calendar_multiget_events/", okuids, baduids)
 
+
     def test_multiget_limited_with_data(self):
         """
         All events.
@@ -82,6 +85,7 @@
         d.addCallbacks(_restoreValueOK, _restoreValueError)
         return d
 
+
     def test_multiget_limited_no_data(self):
         """
         All events.
@@ -103,6 +107,7 @@
 
         return self.simple_event_multiget("/calendar_multiget_events/", okuids, baduids, withData=False)
 
+
     @todo("Remove: Does not work with new store")
     @inlineCallbacks
     def test_multiget_one_broken_event(self):
@@ -110,10 +115,10 @@
         All events.
         (CalDAV-access-09, section 7.6.8)
         """
-        okuids = ["good", "bad",]
+        okuids = ["good", "bad", ]
         baduids = []
         data = {
-            "good":"""BEGIN:VCALENDAR
+            "good": """BEGIN:VCALENDAR
 CALSCALE:GREGORIAN
 PRODID:-//Apple Computer\, Inc//iCal 2.0//EN
 VERSION:2.0
@@ -127,7 +132,7 @@
 END:VEVENT
 END:VCALENDAR
 """.replace("\n", "\r\n"),
-            "bad":"""BEGIN:VCALENDAR
+            "bad": """BEGIN:VCALENDAR
 CALSCALE:GREGORIAN
 PRODID:-//Apple Computer\, Inc//iCal 2.0//EN
 VERSION:2.0
@@ -144,7 +149,7 @@
         }
 
         yield self.simple_event_multiget("/calendar_multiget_events/", okuids, baduids, data)
-        
+
         # Now forcibly corrupt one piece of calendar data
         calendar_path = os.path.join(self.docroot, "calendar_multiget_events/", "bad.ics")
         f = open(calendar_path, "w")
@@ -165,7 +170,10 @@
         baduids = ["bad", ]
         yield self.simple_event_multiget("/calendar_multiget_events/", okuids, baduids, data, no_init=True)
 
+
     def simple_event_multiget(self, cal_uri, okuids, baduids, data=None, no_init=False, withData=True):
+
+        cal_uri = joinURL("/calendars/users/wsanchez", cal_uri)
         props = (
             davxml.GETETag(),
         )
@@ -175,14 +183,14 @@
             )
         children = []
         children.append(davxml.PropertyContainer(*props))
-        
-        okhrefs = [cal_uri + x + ".ics" for x in okuids]
-        badhrefs = [cal_uri + x + ".ics" for x in baduids]
+
+        okhrefs = [joinURL(cal_uri, x + ".ics") for x in okuids]
+        badhrefs = [joinURL(cal_uri, x + ".ics") for x in baduids]
         for href in okhrefs + badhrefs:
             children.append(davxml.HRef.fromString(href))
-        
+
         query = caldavxml.CalendarMultiGet(*children)
-        
+
         def got_xml(doc):
             if not isinstance(doc.root_element, davxml.MultiStatus):
                 self.fail("REPORT response XML root element is not multistatus: %r" % (doc.root_element,))
@@ -200,7 +208,8 @@
 
                     for property in properties:
                         qname = property.qname()
-                        if qname == (davxml.dav_namespace, "getetag"): continue
+                        if qname == (davxml.dav_namespace, "getetag"):
+                            continue
                         if qname != (caldavxml.caldav_namespace, "calendar-data"):
                             self.fail("Response included unexpected property %r" % (property,))
 
@@ -223,7 +232,7 @@
                             original_calendar = ical.Component.fromStream(original_filename)
 
                         self.assertEqual(result_calendar, original_calendar)
-            
+
             for response in doc.root_element.childrenOfType(davxml.StatusResponse):
                 href = str(response.childOfType(davxml.HRef))
                 propstatus = response.childOfType(davxml.PropertyStatus)
@@ -241,38 +250,37 @@
                             continue
                         else:
                             self.fail("Got unexpected href %r" % (href,))
-        
+
             if withData and (len(okuids) + len(badhrefs)):
                 self.fail("Some components were not returned: %r, %r" % (okuids, badhrefs))
 
         return self.calendar_query(cal_uri, query, got_xml, data, no_init)
 
+
     @inlineCallbacks
     def calendar_query(self, calendar_uri, query, got_xml, data, no_init):
 
         if not no_init:
-            response = yield self.send(SimpleRequest(self.site, "MKCALENDAR",
-                calendar_uri))
+            response = yield self.send(SimpleStoreRequest(self, "MKCALENDAR", calendar_uri, authid="wsanchez"))
             response = IResponse(response)
             if response.code != responsecode.CREATED:
                 self.fail("MKCALENDAR failed: %s" % (response.code,))
 
             if data:
                 for filename, icaldata in data.iteritems():
-                    request = SimpleRequest(self.site, "PUT",
-                        calendar_uri + "/" + filename + ".ics")
+                    request = SimpleStoreRequest(self, "PUT", joinURL(calendar_uri, filename + ".ics"), authid="wsanchez")
                     request.stream = MemoryStream(icaldata)
                     yield self.send(request)
             else:
                 # Add holiday events to calendar
                 for child in FilePath(self.holidays_dir).children():
-                    if os.path.splitext(child.basename())[1] != ".ics": continue
-                    request = SimpleRequest(self.site, "PUT",
-                        calendar_uri + "/" + child.basename())
+                    if os.path.splitext(child.basename())[1] != ".ics":
+                        continue
+                    request = SimpleStoreRequest(self, "PUT", joinURL(calendar_uri, child.basename()), authid="wsanchez")
                     request.stream = MemoryStream(child.getContent())
                     yield self.send(request)
 
-        request = SimpleRequest(self.site, "REPORT", calendar_uri)
+        request = SimpleStoreRequest(self, "REPORT", calendar_uri, authid="wsanchez")
         request.stream = MemoryStream(query.toxml())
         response = yield self.send(request)
 

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_validation.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_validation.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_validation.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -1,229 +0,0 @@
-##
-# Copyright (c) 2009-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 xml.etree.cElementTree import XML
-
-from twisted.internet.defer import inlineCallbacks
-from twisted.trial.unittest import TestCase
-
-# XXX this should be public, but it isn't, since it's in a test_* module.  Need
-# to address this to use system twisted.
-from twext.web2.test.test_server import SimpleRequest
-from twext.web2.http import HTTPError
-from twext.web2.resource import Resource
-
-from twistedcaldav.config import config
-from twistedcaldav.ical import Component, Property
-from twistedcaldav.method.put_common import StoreCalendarObjectResource
-from twistedcaldav.caldavxml import MaxAttendeesPerInstance
-from twistedcaldav.resource import CalDAVResource
-
-class InMemoryCalendarObjectResource(CalDAVResource):
-
-    def exists(self):
-        return hasattr(self, "_data") and self._data is not None
-
-
-    def iCalendarForUser(self, user):
-        return self._data
-
-
-    def setData(self, data):
-        self._data = data
-
-
-
-class TestCopyMoveValidation(TestCase):
-    """
-    Tests for the validation code in L{twistedcaldav.method.put_common}.
-    """
-
-    def setUp(self):
-        """
-        Set up some CalDAV stuff.
-        """
-
-        self.destination = InMemoryCalendarObjectResource()
-        self.destination.name = lambda : 'bar'
-        self.destinationParent = CalDAVResource()
-        self.destinationParent.name = lambda : 'foo'
-        self.destinationParent.isSupportedComponent = lambda x: True
-
-
-    def _getSampleCalendar(self):
-        return Component.fromString("""BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Apple Computer\, Inc//iCal 2.0//EN
-BEGIN:VEVENT
-UID:12345-67890
-DTSTAMP:20071114T000000Z
-DTSTART:20071114T000000Z
-ORGANIZER:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-RRULE:FREQ=YEARLY
-END:VEVENT
-END:VCALENDAR
-""")
-
-
-    def _getStorer(self, calendar):
-        self.sampleCalendar = calendar
-        req = SimpleRequest(None, "COPY", "http://example.com/foo/bar")
-        req._rememberResource(self.destination, "/foo/bar")
-        req._rememberResource(self.destinationParent, "/foo/")
-        req._rememberResource(Resource(), "/")
-        self.storer = StoreCalendarObjectResource(
-            req,
-            destination=self.destination,
-            destinationparent=self.destinationParent,
-            destination_uri="http://example.com/foo/baz",
-            calendar=self.sampleCalendar
-        )
-        return self.storer
-
-
-    @inlineCallbacks
-    def test_simpleValidRequest(self):
-        """
-        For a simple valid request,
-        L{StoreCalendarObjectResource.fullValidation} results in a L{Deferred}
-        which fires with C{None} (and raises no exception).
-        """
-        self.assertEquals((yield self._getStorer(self._getSampleCalendar()).fullValidation()), None)
-
-
-    @inlineCallbacks
-    def test_exceedMaximumAttendeesIfNew(self):
-        """
-        If too many attendees are specified (more than the configured maximum
-        for the server), the storer raises an exception containing a
-        L{MaxAttendeesPerInstance} element that reports the maximum value, as
-        per U{RFC4791 section 5.2.9
-        <http://www.webdav.org/specs/rfc4791.html#max-attendees-per-instance>}.
-        This test is for new resources.
-        """
-
-        # Get the event, and add too many attendees to it.
-        self.sampleCalendar = self._getSampleCalendar()
-        eventComponent = list(self.sampleCalendar.subcomponents())[0]
-        for x in xrange(config.MaxAttendeesPerInstance):
-            eventComponent.addProperty(
-                Property("ATTENDEE", "mailto:user%d at example.com" % (x + 3,)))
-
-        try:
-            yield self._getStorer(self.sampleCalendar).fullValidation()
-        except HTTPError, err:
-            element = XML(err.response.stream.mem)[0]
-            self.assertEquals(
-                element.tag,
-                MaxAttendeesPerInstance.sname()
-            )
-            self.assertEquals(int(element.text), config.MaxAttendeesPerInstance)
-        else:
-            self.fail("No error; validation should have failed!")
-
-
-    @inlineCallbacks
-    def test_exceedMaximumAttendeesWhenIncreasing(self):
-        """
-        If too many attendees are specified (more than the configured maximum
-        for the server), the storer raises an exception containing a
-        L{MaxAttendeesPerInstance} element that reports the maximum value, as
-        per U{RFC4791 section 5.2.9
-        <http://www.webdav.org/specs/rfc4791.html#max-attendees-per-instance>}.
-        This test is for an increase to an already over-sized resource.
-        """
-
-        self.patch(config, "MaxAttendeesPerInstance", config.MaxAttendeesPerInstance + 10)
-
-        # Get the event, and add many attendees to it - but not enough to fail.
-        self.sampleCalendar = self._getSampleCalendar()
-        eventComponent = list(self.sampleCalendar.subcomponents())[0]
-        for x in xrange(config.MaxAttendeesPerInstance - 5):
-            eventComponent.addProperty(
-                Property("ATTENDEE", "mailto:user%d at example.com" % (x + 3,)))
-
-        try:
-            yield self._getStorer(self.sampleCalendar).fullValidation()
-        except HTTPError:
-            self.fail("Validation should not have failed!")
-        self.destination.setData(self.sampleCalendar.duplicate())
-
-        # Now reduce the limit and try to add an attendee.
-        config.MaxAttendeesPerInstance -= 10
-        eventComponent.addProperty(
-            Property("ATTENDEE", "mailto:user-extra at example.com"))
-
-        try:
-            yield self._getStorer(self.sampleCalendar).fullValidation()
-        except HTTPError, err:
-            element = XML(err.response.stream.mem)[0]
-            self.assertEquals(
-                element.tag,
-                MaxAttendeesPerInstance.sname()
-            )
-            self.assertEquals(int(element.text), config.MaxAttendeesPerInstance)
-        else:
-            self.fail("No error; validation should have failed!")
-
-
-    @inlineCallbacks
-    def test_doNotExceedMaximumAttendeesIfAlreadyPresent(self):
-        """
-        If too many attendees are specified (more than the configured maximum
-        for the server), the storer raises an exception containing a
-        L{MaxAttendeesPerInstance} element that reports the maximum value, as
-        per U{RFC4791 section 5.2.9
-        <http://www.webdav.org/specs/rfc4791.html#max-attendees-per-instance>}.
-        This test is for no change to an already over-sized resource.
-        """
-
-        self.patch(config, "MaxAttendeesPerInstance", config.MaxAttendeesPerInstance + 10)
-
-        # Get the event, and add many attendees to it - but not enough to fail.
-        self.sampleCalendar = self._getSampleCalendar()
-        eventComponent = list(self.sampleCalendar.subcomponents())[0]
-        for x in xrange(config.MaxAttendeesPerInstance - 5):
-            eventComponent.addProperty(
-                Property("ATTENDEE", "mailto:user%d at example.com" % (x + 3,)))
-
-        try:
-            yield self._getStorer(self.sampleCalendar).fullValidation()
-        except HTTPError:
-            self.fail("Validation should not have failed!")
-        self.destination.setData(self.sampleCalendar.duplicate())
-
-        # Now reduce the limit and try to store without any additional attendees.
-        config.MaxAttendeesPerInstance -= 10
-
-        try:
-            yield self._getStorer(self.sampleCalendar).fullValidation()
-        except HTTPError:
-            self.fail("Validation should not have failed!")
-        self.destination.setData(self.sampleCalendar.duplicate())
-
-        # Now try to store with fewer attendees.
-        self.sampleCalendar = self._getSampleCalendar()
-        eventComponent = list(self.sampleCalendar.subcomponents())[0]
-        for x in xrange(config.MaxAttendeesPerInstance + 2):
-            eventComponent.addProperty(
-                Property("ATTENDEE", "mailto:user%d at example.com" % (x + 3,)))
-
-        try:
-            yield self._getStorer(self.sampleCalendar).fullValidation()
-        except HTTPError:
-            self.fail("Validation should not have failed!")
-        self.destination.setData(self.sampleCalendar.duplicate())

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_wrapping.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_wrapping.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/test_wrapping.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -19,7 +19,6 @@
 """
 
 
-from twext.web2.server import Request
 from twext.web2.responsecode import UNAUTHORIZED
 from twext.web2.http_headers import Headers
 from twext.enterprise.ienterprise import AlreadyFinishedError
@@ -35,15 +34,14 @@
 from twistedcaldav.storebridge import DropboxCollection, \
     CalendarCollectionResource, AddressBookCollectionResource
 
-from twistedcaldav.test.util import TestCase
+from twistedcaldav.test.util import StoreTestCase, SimpleStoreRequest
 
 from txdav.idav import IDataStore
 from txdav.caldav.datastore.test.test_file import test_event_text
 
 from txdav.carddav.datastore.test.test_file import vcard4_text
 
-from txdav.common.datastore.test.util import buildStore, assertProvides, \
-    StubNotifierFactory
+from txdav.common.datastore.test.util import assertProvides
 
 
 from twext.web2.http import HTTPError
@@ -98,25 +96,13 @@
 
 
 
-class WrappingTests(TestCase):
+class WrappingTests(StoreTestCase):
     """
     Tests for L{twistedcaldav.static.CalDAVResource} creating the appropriate type
     of txdav.caldav.datastore.file underlying object when it can determine what
     type it really represents.
     """
 
-    def setUp(self):
-        # Mostly copied from
-        # twistedcaldav.directory.test.test_calendar.ProvisionedCalendars; this
-        # should probably be refactored, perhaps even to call (some part of) the
-        # actual root-resource construction logic?
-        super(WrappingTests, self).setUp()
-
-        # Setup the initial directory
-        self.createStockDirectoryService()
-        self.setupCalendars()
-
-
     @inlineCallbacks
     def populateOneObject(self, objectName, objectText):
         """
@@ -128,21 +114,13 @@
         @param objectText: Some iCalendar text to populate it with.
         @type objectText: str
         """
-        record = self.directoryService.recordWithShortName("users", "wsanchez")
+        record = self.directory.recordWithShortName("users", "wsanchez")
         uid = record.uid
-        # XXX there should be a more test-friendly way to ensure the directory
-        # actually exists
-        try:
-            self.calendarCollection._newStore._path.createDirectory()
-        except:
-            pass
-        txn = self.calendarCollection._newStore.newTransaction()
+        txn = self.transactionUnderTest()
         home = yield txn.calendarHomeWithUID(uid, True)
         cal = yield home.calendarWithName("calendar")
-        yield cal.createCalendarObjectWithName(
-            objectName, VComponent.fromString(objectText)
-        )
-        yield txn.commit()
+        yield cal.createCalendarObjectWithName(objectName, VComponent.fromString(objectText))
+        yield self.commit()
 
 
     @inlineCallbacks
@@ -155,24 +133,13 @@
         @param objectText: Some iVcard text to populate it with.
         @type objectText: str
         """
-        record = self.directoryService.recordWithShortName("users", "wsanchez")
+        record = self.directory.recordWithShortName("users", "wsanchez")
         uid = record.uid
-        # XXX there should be a more test-friendly way to ensure the directory
-        # actually exists
-        try:
-            self.addressbookCollection._newStore._path.createDirectory()
-        except:
-            pass
-        txn = self.addressbookCollection._newStore.newTransaction()
+        txn = self.transactionUnderTest()
         home = yield txn.addressbookHomeWithUID(uid, True)
         adbk = yield home.addressbookWithName("addressbook")
-        if adbk is None:
-            yield home.createAddressBookWithName("addressbook")
-            adbk = yield home.addressbookWithName("addressbook")
-        yield adbk.createAddressBookObjectWithName(
-            objectName, VCComponent.fromString(objectText)
-        )
-        yield txn.commit()
+        yield adbk.createAddressBookObjectWithName(objectName, VCComponent.fromString(objectText))
+        yield self.commit()
 
     requestUnderTest = None
 
@@ -212,36 +179,18 @@
         returnValue(aResource)
 
 
-    def commit(self):
-        """
-        Since C{getResource} treats this test case as a resource, it will have
-        an associated transaction.  Commit that transaction to bring the
-        filesystem into a consistent state.
-        """
-        return self.requestUnderTest._newStoreTransaction.commit()
-
-
     def requestForPath(self, path, method='GET'):
         """
         Get a L{Request} with a L{FakeChanRequest} for a given path and method.
         """
         headers = Headers()
         headers.addRawHeader("Host", "localhost:8008")
-        chanReq = FakeChanRequest()
-        req = Request(
-            site=self.site,
-            chanRequest=chanReq,
-            command=method,
-            path=path,
-            version=('1', '1'),
-            contentLength=0,
-            headers=headers
-        )
+        req = SimpleStoreRequest(self, method, path, headers)
 
         # 'process()' normally sets these.  Shame on web2, having so much
         # partially-initialized stuff floating around.
         req.remoteAddr = '127.0.0.1'
-        req.path = path
+        req.chanRequest = FakeChanRequest()
         req.credentialFactories = {}
         return req
 
@@ -254,7 +203,7 @@
         L{Resource} is accurately set.
         """
         self.assertEquals(resource._principalCollections,
-                          frozenset([self.directoryFixture.principalsResource]))
+                          frozenset([self.principalsResource]))
 
 
     @inlineCallbacks
@@ -268,10 +217,11 @@
         for pathType in self.pathTypes:
             req = self.requestForPath('/%ss/users/wsanchez/%s/forget/it'
                                       % (pathType, pathType))
+            txn = self.transactionUnderTest()
             yield req.process()
             self.assertEquals(req.chanRequest.code, 404)
             yield self.failUnlessFailure(
-                maybeDeferred(req._newStoreTransaction.commit),
+                maybeDeferred(txn.commit),
                 AlreadyFinishedError
             )
 
@@ -294,7 +244,7 @@
         Creating a DirectoryCalendarHomeProvisioningResource will create a
         paired CalendarStore.
         """
-        assertProvides(self, IDataStore, self.calendarCollection._newStore)
+        assertProvides(self, IDataStore, self._sqlCalendarStore)
 
 
     @inlineCallbacks
@@ -424,9 +374,9 @@
         Exceeding quota on an attachment returns an HTTP error code.
         """
         self.patch(config, "EnableDropBox", True)
-        if not hasattr(self.calendarCollection._newStore, "_dropbox_ok"):
-            self.calendarCollection._newStore._dropbox_ok = False
-        self.patch(self.calendarCollection._newStore, "_dropbox_ok", True)
+        if not hasattr(self._sqlCalendarStore, "_dropbox_ok"):
+            self._sqlCalendarStore._dropbox_ok = False
+        self.patch(self._sqlCalendarStore, "_dropbox_ok", True)
         self.patch(Calendar, "asShared", lambda self: [])
 
         yield self.populateOneObject("1.ics", test_event_text)
@@ -545,8 +495,7 @@
         """
         Assert that a user's calendar is empty (their default calendar by default).
         """
-        txn = self.calendarStore.newTransaction()
-        self.addCleanup(txn.commit)
+        txn = self.transactionUnderTest()
         home = yield txn.calendarHomeWithUID(user, create=True)
         cal = yield home.calendarWithName(calendarName)
         objects = yield cal.calendarObjects()
@@ -557,16 +506,6 @@
 class DatabaseWrappingTests(WrappingTests):
 
     @inlineCallbacks
-    def setUp(self):
-        self.calendarStore = yield buildStore(self, StubNotifierFactory())
-        super(DatabaseWrappingTests, self).setUp()
-
-
-    def createDataStore(self):
-        return self.calendarStore
-
-
-    @inlineCallbacks
     def test_invalidCalendarPUT(self):
         """
         Exceeding quota on an attachment returns an HTTP error code.
@@ -583,6 +522,7 @@
                 ((yield calendarObject.renderHTTP(self.requestUnderTest)),
                  self.requestUnderTest)
             )
+
         # see twistedcaldav/directory/test/accounts.xml
         wsanchez = '6423F94A-6B76-4A3A-815B-D52CFD77935D'
         cdaboo = '5A985493-EE2C-4665-94CF-4DFEA3A89500'
@@ -628,9 +568,10 @@
 DURATION:PT1H
 SUMMARY:Test
 END:VEVENT""".format(wsanchez=wsanchez, cdaboo=cdaboo)
-        #txn = self.requestUnderTest._newStoreTransaction
+
         invalidEvent = eventTemplate.format(invalidInstance, wsanchez=wsanchez, cdaboo=cdaboo).replace(CR, CRLF)
         yield putEvt(invalidEvent)
+        self.lastTransaction = None
         self.requestUnderTest = None
         yield self.assertCalendarEmpty(wsanchez)
         yield self.assertCalendarEmpty(cdaboo)

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/util.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/test/util.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -29,7 +29,7 @@
 from twext.python.memcacheclient import ClientFactory
 from twext.python.filepath import CachingFilePath as FilePath
 import twext.web2.dav.test.util
-from txdav.xml import element as davxml
+from txdav.xml import element as davxml, element
 from twext.web2.http import HTTPError, StatusResponse
 
 from twistedcaldav import memcacher
@@ -44,12 +44,17 @@
 from twistedcaldav.directory.aggregate import AggregateDirectoryService
 from twistedcaldav.directory.xmlfile import XMLDirectoryService
 
-from txdav.common.datastore.test.util import deriveQuota
+from txdav.common.datastore.test.util import deriveQuota, CommonCommonTests
 from txdav.common.datastore.file import CommonDataStore
 
 from calendarserver.provision.root import RootResource
 
 from twext.python.log import Logger
+from txdav.caldav.datastore.test.util import buildCalendarStore
+from calendarserver.tap.util import getRootResource, directoryFromConfig
+from twext.web2.dav.test.util import SimpleRequest
+from twistedcaldav.directory.util import transactionFromRequest
+from twistedcaldav.directory.directory import DirectoryService
 
 log = Logger()
 
@@ -95,7 +100,6 @@
             )
         self._directoryChangeHooks = [_setUpPrincipals]
 
-
     directoryService = None
     principalsResource = None
 
@@ -150,6 +154,119 @@
 
 
 
+class SimpleStoreRequest(SimpleRequest):
+    """
+    A SimpleRequest that automatically grabs the proper transaction for a test.
+    """
+    def __init__(self, test, method, uri, headers=None, content=None, authid=None):
+        super(SimpleStoreRequest, self).__init__(test.site, method, uri, headers, content)
+        self._test = test
+        self._newStoreTransaction = test.transactionUnderTest(txn=transactionFromRequest(self, test.storeUnderTest()))
+        self.credentialFactories = {}
+
+        # Fake credentials if auth needed
+        if authid is not None:
+            record = self._test.directory.recordWithShortName(DirectoryService.recordType_users, authid)
+            if record:
+                self.authzUser = self.authnUser = element.Principal(element.HRef("/principals/__uids__/%s/" % (record.uid,)))
+
+
+    @inlineCallbacks
+    def process(self):
+        """
+        Process will commit the transaction in the test so we need to clear it out.
+        """
+        result = yield super(SimpleStoreRequest, self).process()
+        self._test.lastTransaction = None
+        returnValue(result)
+
+
+    def _cbFinishRender(self, result):
+        self._test.lastTransaction = None
+        return super(SimpleStoreRequest, self)._cbFinishRender(result)
+
+
+
+class StoreTestCase(CommonCommonTests, twext.web2.dav.test.util.TestCase):
+    """
+    A base class for tests that use the SQL store.
+    """
+
+    @inlineCallbacks
+    def setUp(self):
+        yield super(StoreTestCase, self).setUp()
+
+        self.configure()
+
+        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory, directoryFromConfig(config))
+        self.rootResource = getRootResource(config, self._sqlCalendarStore)
+        self.directory = self._sqlCalendarStore.directoryService()
+
+        self.principalsResource = DirectoryPrincipalProvisioningResource("/principals/", self.directory)
+        self.site.resource.putChild("principals", self.principalsResource)
+        self.calendarCollection = DirectoryCalendarHomeProvisioningResource(self.directory, "/calendars/", self._sqlCalendarStore)
+        self.site.resource.putChild("calendars", self.calendarCollection)
+        self.addressbookCollection = DirectoryAddressBookHomeProvisioningResource(self.directory, "/addressbooks/", self._sqlCalendarStore)
+        self.site.resource.putChild("addressbooks", self.addressbookCollection)
+
+        yield self.populate()
+
+
+    def populate(self):
+        return succeed(None)
+
+
+    def storeUnderTest(self):
+        """
+        Return a store for testing.
+        """
+        return self._sqlCalendarStore
+
+
+    def configure(self):
+        """
+        Adjust the global configuration for this test.
+        """
+        self.serverRoot = self.mktemp()
+        os.mkdir(self.serverRoot)
+
+        config.reset()
+
+        config.ServerRoot = os.path.abspath(self.serverRoot)
+        config.ConfigRoot = "config"
+        config.LogRoot = "logs"
+        config.RunRoot = "logs"
+
+        if not os.path.exists(config.DataRoot):
+            os.makedirs(config.DataRoot)
+        if not os.path.exists(config.DocumentRoot):
+            os.makedirs(config.DocumentRoot)
+        if not os.path.exists(config.ConfigRoot):
+            os.makedirs(config.ConfigRoot)
+        if not os.path.exists(config.LogRoot):
+            os.makedirs(config.LogRoot)
+
+        config.Memcached.Pools.Default.ClientEnabled = False
+        config.Memcached.Pools.Default.ServerEnabled = False
+        ClientFactory.allowTestCache = True
+        memcacher.Memcacher.allowTestCache = True
+        memcacher.Memcacher.memoryCacheInstance = None
+        config.DirectoryAddressBook.Enabled = False
+
+        accounts = FilePath(config.DataRoot).child("accounts.xml")
+        accounts.setContent(xmlFile.getContent())
+
+
+    @property
+    def directoryService(self):
+        """
+        Read-only alias for L{DirectoryFixture.directoryService} for
+        compatibility with older tests.  TODO: remove this.
+        """
+        return self.directory
+
+
+
 class TestCase(twext.web2.dav.test.util.TestCase):
     resource_class = RootResource
 
@@ -159,7 +276,7 @@
         addressbooks.)  By default returns a L{CommonDataStore}, but this is a
         hook for subclasses to override to provide different data stores.
         """
-        return CommonDataStore(FilePath(config.DocumentRoot), None, True, False,
+        return CommonDataStore(FilePath(config.DocumentRoot), None, None, True, False,
                                quota=deriveQuota(self))
 
 
@@ -267,7 +384,7 @@
                     continue
 
                 childPath = os.path.join(parent, childName)
-                if childStructure.has_key("@contents"):
+                if "@contents" in childStructure:
                     # This is a file
                     with open(childPath, "w") as child:
                         child.write(childStructure["@contents"])
@@ -276,7 +393,7 @@
                     os.mkdir(childPath)
                     createChildren(childPath, childStructure)
 
-                if childStructure.has_key("@xattrs"):
+                if "@xattrs" in childStructure:
                     xattrs = childStructure["@xattrs"]
                     for attr, value in xattrs.iteritems():
                         try:
@@ -285,7 +402,7 @@
                             pass
 
                 # Set access and modified times
-                if childStructure.has_key("@timestamp"):
+                if "@timestamp" in childStructure:
                     timestamp = childStructure["@timestamp"]
                     os.utime(childPath, (timestamp, timestamp))
 
@@ -338,13 +455,13 @@
                 childPath = os.path.join(parent, childName)
 
                 if not os.path.exists(childPath):
-                    if childStructure.has_key("@optional"):
+                    if "@optional" in childStructure:
                         return True
                     else:
                         print("Missing:", childPath)
                         return False
 
-                if childStructure.has_key("@contents"):
+                if "@contents" in childStructure:
                     # This is a file
                     expectedContents = childStructure["@contents"]
                     if expectedContents is None:
@@ -371,7 +488,7 @@
                     if not verifyChildren(childPath, childStructure):
                         return False
 
-                if childStructure.has_key("@xattrs"):
+                if "@xattrs" in childStructure:
                     try:
                         # See if we have xattr support; IOError if not
                         try:
@@ -406,11 +523,14 @@
 
         return verifyChildren(root, structure)
 
+
+
 class norequest(object):
     def addResponseFilter(self, filter):
         "stub; ignore me"
 
 
+
 class HomeTestCase(TestCase):
     """
     Utility class for tests which wish to interact with a calendar home rather
@@ -421,7 +541,7 @@
         # FIXME: AddressBookHomeTestCase needs the same treatment.
         fp = FilePath(self.mktemp())
         fp.createDirectory()
-        return CommonDataStore(fp, None, True, False)
+        return CommonDataStore(fp, None, None, True, False)
 
 
     def setUp(self):
@@ -449,7 +569,6 @@
 
         return self._refreshRoot().addCallback(_defer)
 
-
     committed = True
 
     def noRenderCommit(self):
@@ -577,6 +696,7 @@
         self._properties = {}
         self.resource = _FauxResource()
 
+
     def get(self, qname, uid=None):
         qnameuid = qname + (uid,)
         data = self._properties.get(qnameuid)
@@ -584,10 +704,12 @@
             raise HTTPError(StatusResponse(404, "No such property"))
         return data
 
+
     def set(self, property, uid=None):
         qnameuid = property.qname() + (uid,)
         self._properties[qnameuid] = property
 
+
     def delete(self, qname, uid=None):
         try:
             qnameuid = qname + (uid,)
@@ -595,14 +717,16 @@
         except KeyError:
             pass
 
+
     def contains(self, qname, uid=None):
         qnameuid = qname + (uid,)
         return qnameuid in self._properties
 
+
     def list(self, uid=None, filterByUID=True):
         results = self._properties.iterkeys()
         if filterByUID:
-            return [ 
+            return [
                 (namespace, name)
                 for namespace, name, propuid in results
                 if propuid == uid
@@ -611,6 +735,7 @@
             return results
 
 
+
 class StubCacheChangeNotifier(object):
     def __init__(self, *args, **kwargs):
         pass
@@ -634,6 +759,7 @@
 
         self._timeouts = {}
 
+
     def get(self, key):
         if key not in self._cache:
             return succeed((0, None))
@@ -649,7 +775,6 @@
             if key in self._timeouts:
                 self._timeouts[key].cancel()
 
-
             self._timeouts[key] = self._reactor.callLater(
                 expireTime,
                 _removeKey)
@@ -693,6 +818,7 @@
     """
 
 
+
 class CapturingProcessProtocol(ProcessProtocol):
     """
     A L{ProcessProtocol} that captures its output and error.

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/base/propertystore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/base/propertystore/sql.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/base/propertystore/sql.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -50,7 +50,6 @@
             "do not construct directly, call PropertyStore.load()"
         )
 
-
     _allWithID = Select([prop.NAME, prop.VIEWER_UID, prop.VALUE],
                         From=prop,
                         Where=prop.RESOURCE_ID == Parameter("resourceID"))
@@ -213,14 +212,12 @@
 
         return WebDAVDocument.fromString(value).root_element
 
-
     _updateQuery = Update({prop.VALUE: Parameter("value")},
                           Where=(
                               prop.RESOURCE_ID == Parameter("resourceID")).And(
                               prop.NAME == Parameter("name")).And(
                               prop.VIEWER_UID == Parameter("uid")))
 
-
     _insertQuery = Insert({prop.VALUE: Parameter("value"),
                            prop.RESOURCE_ID: Parameter("resourceID"),
                            prop.NAME: Parameter("name"),
@@ -262,8 +259,6 @@
             self.log_error("setting a property failed; probably nothing.")
         self._txn.subtransaction(trySetItem).addErrback(justLogIt)
 
-
-
     _deleteQuery = Delete(
         prop, Where=(prop.RESOURCE_ID == Parameter("resourceID")).And(
             prop.NAME == Parameter("name")).And(
@@ -276,11 +271,17 @@
 
         key_str = key.toString()
         del self._cached[(key_str, uid)]
-        self._deleteQuery.on(self._txn, lambda:KeyError(key),
-                             resourceID=self._resourceID,
-                             name=key_str, uid=uid
-                            )
-        self._cacher.delete(str(self._resourceID))
+        @inlineCallbacks
+        def doIt(txn):
+            yield self._deleteQuery.on(txn, lambda: KeyError(key),
+                                 resourceID=self._resourceID,
+                                 name=key_str, uid=uid
+                                )
+            self._cacher.delete(str(self._resourceID))
+        def justLogIt(f):
+            f.trap(AllRetriesFailed)
+            self.log_error("setting a property failed; probably nothing.")
+        self._txn.subtransaction(doIt).addErrback(justLogIt)
 
 
     def _keys_uid(self, uid):
@@ -298,6 +299,7 @@
         self._deleteResourceQuery.on(self._txn, resourceID=self._resourceID)
         self._cacher.delete(str(self._resourceID))
 
+
     @inlineCallbacks
     def copyAllProperties(self, other):
         """
@@ -316,7 +318,6 @@
                 yield self._insertQuery.on(
                     self._txn, resourceID=self._resourceID, value=value_str,
                     name=key_str, uid=uid)
-                
 
         # Reload from the DB
         self._cached = {}

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/base/propertystore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/base/propertystore/test/test_sql.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/base/propertystore/test/test_sql.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -148,6 +148,7 @@
                   'b': pval2}[race[-1]]
         self.assertEquals(self.propertyStore[pname], winner)
 
+
     @inlineCallbacks
     def test_copy(self):
 
@@ -182,7 +183,7 @@
 
         # Do copy and check results
         yield store2_user1.copyAllProperties(store1_user1)
-        
+
         self.assertEqual(store1_user1.keys(), store2_user1.keys())
 
         store1_user2 = yield PropertyStore.load("user01", "user02", self._txn, 2)
@@ -190,7 +191,23 @@
         self.assertEqual(store1_user2.keys(), store2_user2.keys())
 
 
+    @inlineCallbacks
+    def test_insert_delete(self):
+
+        # Existing store
+        store1_user1 = yield PropertyStore.load("user01", None, self._txn, 2)
+
+        pname = propertyName("dummy1")
+        pvalue = propertyValue("value1-user1")
+
+        yield store1_user1.__setitem__(pname, pvalue)
+        self.assertEqual(store1_user1[pname], pvalue)
+
+        yield store1_user1.__delitem__(pname)
+        self.assertTrue(pname not in store1_user1)
+
+        yield store1_user1.__setitem__(pname, pvalue)
+        self.assertEqual(store1_user1[pname], pvalue)
+
 if PropertyStore is None:
     PropertyStoreTest.skip = importErrorMessage
-
-

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/resource.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/resource.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -1,516 +0,0 @@
-# -*- test-case-name: twistedcaldav.directory.test.test_calendar -*-
-##
-# Copyright (c) 2005-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.
-##
-
-"""
-CalDAV scheduling resources.
-"""
-
-__all__ = [
-    "ScheduleInboxResource",
-    "ScheduleOutboxResource",
-    "deliverSchedulePrivilegeSet",
-]
-
-
-from twext.web2 import responsecode
-from txdav.xml import element as davxml
-from txdav.xml.rfc2518 import HRef
-from twext.web2.dav.http import ErrorResponse, MultiStatusResponse
-from twext.web2.dav.resource import davPrivilegeSet
-from twext.web2.dav.util import joinURL, normalizeURL
-from twext.web2.http import HTTPError
-
-from twisted.internet.defer import inlineCallbacks, returnValue, succeed
-
-from twistedcaldav import caldavxml, customxml
-from twistedcaldav.caldavxml import caldav_namespace, Opaque, \
-    CalendarFreeBusySet, ScheduleCalendarTransp
-from twistedcaldav.config import config
-# _schedulePrivilegeSet implicitly depends on config being initialized. The
-# following line is wrong because _schedulePrivilegeSet won't actually use the
-# config file, it will pick up stdconfig whenever it is imported, so this works
-# around that for now.
-__import__("twistedcaldav.stdconfig") # FIXME
-from twistedcaldav.customxml import calendarserver_namespace
-from twistedcaldav.ical import allowedComponents
-from twistedcaldav.resource import CalDAVResource
-from twistedcaldav.resource import isCalendarCollectionResource
-from txdav.caldav.datastore.scheduling.caldav.scheduler import CalDAVScheduler
-
-from txdav.base.propertystore.base import PropertyName
-
-def _schedulePrivilegeSet(deliver):
-    edited = False
-
-    top_supported_privileges = []
-
-    for supported_privilege in davPrivilegeSet.childrenOfType(davxml.SupportedPrivilege):
-        all_privilege = supported_privilege.childOfType(davxml.Privilege)
-        if isinstance(all_privilege.children[0], davxml.All):
-            all_description = supported_privilege.childOfType(davxml.Description)
-            all_supported_privileges = list(supported_privilege.childrenOfType(davxml.SupportedPrivilege))
-            all_supported_privileges.append(
-                davxml.SupportedPrivilege(
-                    davxml.Privilege(caldavxml.ScheduleDeliver() if deliver else caldavxml.ScheduleSend()),
-                    davxml.Description("schedule privileges for current principal", **{"xml:lang": "en"}),
-                ),
-            )
-            if config.Scheduling.CalDAV.OldDraftCompatibility:
-                all_supported_privileges.append(
-                    davxml.SupportedPrivilege(
-                        davxml.Privilege(caldavxml.Schedule()),
-                        davxml.Description("old-style schedule privileges for current principal", **{"xml:lang": "en"}),
-                    ),
-                )
-            top_supported_privileges.append(
-                davxml.SupportedPrivilege(all_privilege, all_description, *all_supported_privileges)
-            )
-            edited = True
-        else:
-            top_supported_privileges.append(supported_privilege)
-
-    assert edited, "Structure of davPrivilegeSet changed in a way that I don't know how to extend for schedulePrivilegeSet"
-
-    return davxml.SupportedPrivilegeSet(*top_supported_privileges)
-
-deliverSchedulePrivilegeSet = _schedulePrivilegeSet(True)
-sendSchedulePrivilegeSet = _schedulePrivilegeSet(False)
-
-class CalendarSchedulingCollectionResource (CalDAVResource):
-    """
-    CalDAV principal resource.
-
-    Extends L{DAVResource} to provide CalDAV scheduling collection
-    functionality.
-    """
-    def __init__(self, parent):
-        """
-        @param parent: the parent resource of this one.
-        """
-        assert parent is not None
-
-        super(CalendarSchedulingCollectionResource, self).__init__(principalCollections=parent.principalCollections())
-
-        self.parent = parent
-
-
-    def isCollection(self):
-        return True
-
-
-    def isCalendarCollection(self):
-        return False
-
-
-    def isPseudoCalendarCollection(self):
-        return True
-
-
-    def supportedReports(self):
-        result = super(CalDAVResource, self).supportedReports()
-        result.append(davxml.Report(caldavxml.CalendarQuery(),))
-        result.append(davxml.Report(caldavxml.CalendarMultiGet(),))
-        # free-busy report not allowed
-        if config.EnableSyncReport:
-            # Only allowed on calendar/inbox/addressbook collections
-            result.append(davxml.Report(davxml.SyncCollection(),))
-        return result
-
-
-
-class ScheduleInboxResource (CalendarSchedulingCollectionResource):
-    """
-    CalDAV schedule Inbox resource.
-
-    Extends L{DAVResource} to provide CalDAV functionality.
-    """
-
-    def liveProperties(self):
-
-        return super(ScheduleInboxResource, self).liveProperties() + (
-            caldavxml.CalendarFreeBusySet.qname(),
-            caldavxml.ScheduleDefaultCalendarURL.qname(),
-            customxml.ScheduleDefaultTasksURL.qname(),
-        )
-
-
-    def resourceType(self):
-        return davxml.ResourceType.scheduleInbox
-
-
-    @inlineCallbacks
-    def readProperty(self, property, request):
-        if type(property) is tuple:
-            qname = property
-        else:
-            qname = property.qname()
-
-        if qname == caldavxml.CalendarFreeBusySet.qname():
-            # Always return at least an empty list
-            if not self.hasDeadProperty(property):
-                top = self.parent.url()
-                values = []
-                for cal in (yield self.parent._newStoreHome.calendars()):
-                    prop = cal.properties().get(PropertyName.fromString(ScheduleCalendarTransp.sname()))
-                    if prop == ScheduleCalendarTransp(Opaque()):
-                        values.append(HRef(joinURL(top, cal.name())))
-                returnValue(CalendarFreeBusySet(*values))
-        elif qname in (caldavxml.ScheduleDefaultCalendarURL.qname(), customxml.ScheduleDefaultTasksURL.qname()):
-            result = (yield self.readDefaultCalendarProperty(request, qname))
-            returnValue(result)
-
-        result = (yield super(ScheduleInboxResource, self).readProperty(property, request))
-        returnValue(result)
-
-
-    @inlineCallbacks
-    def writeProperty(self, property, request):
-        assert isinstance(property, davxml.WebDAVElement)
-
-        # Strictly speaking CS:calendar-availability is a live property in the sense that the
-        # server enforces what can be stored, however it need not actually
-        # exist so we cannot list it in liveProperties on this resource, since its
-        # its presence there means that hasProperty will always return True for it.
-        if property.qname() == customxml.CalendarAvailability.qname():
-            if not property.valid():
-                raise HTTPError(ErrorResponse(
-                    responsecode.CONFLICT,
-                    (caldav_namespace, "valid-calendar-data"),
-                    description="Invalid property"
-                ))
-
-        elif property.qname() == caldavxml.CalendarFreeBusySet.qname():
-            # Verify that the calendars added in the PROPPATCH are valid. We do not check
-            # whether existing items in the property are still valid - only new ones.
-            property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children]
-            new_calendars = set([str(href) for href in property.children])
-            if not self.hasDeadProperty(property):
-                old_calendars = set()
-            else:
-                old_calendars = set([normalizeURL(str(href)) for href in self.readDeadProperty(property).children])
-            added_calendars = new_calendars.difference(old_calendars)
-            for href in added_calendars:
-                cal = (yield request.locateResource(str(href)))
-                if cal is None or not cal.exists() or not isCalendarCollectionResource(cal):
-                    # Validate that href's point to a valid calendar.
-                    raise HTTPError(ErrorResponse(
-                        responsecode.CONFLICT,
-                        (caldav_namespace, "valid-calendar-url"),
-                        "Invalid URI",
-                    ))
-            for href in tuple(new_calendars):
-                cal = (yield request.locateResource(str(href)))
-                if cal is None or not cal.exists() or not isCalendarCollectionResource(cal):
-                    new_calendars.remove(href)
-            property.children = [davxml.HRef(href) for href in new_calendars]
-
-        elif property.qname() in (caldavxml.ScheduleDefaultCalendarURL.qname(), customxml.ScheduleDefaultTasksURL.qname()):
-            property = (yield self.writeDefaultCalendarProperty(request, property))
-
-        yield super(ScheduleInboxResource, self).writeProperty(property, request)
-
-
-    def processFreeBusyCalendar(self, uri, addit):
-        uri = normalizeURL(uri)
-
-        if not self.hasDeadProperty(caldavxml.CalendarFreeBusySet.qname()):
-            fbset = set()
-        else:
-            fbset = set([normalizeURL(str(href)) for href in self.readDeadProperty(caldavxml.CalendarFreeBusySet.qname()).children])
-        if addit:
-            if uri not in fbset:
-                fbset.add(uri)
-                self.writeDeadProperty(caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in fbset]))
-        else:
-            if uri in fbset:
-                fbset.remove(uri)
-                self.writeDeadProperty(caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in fbset]))
-
-
-    @inlineCallbacks
-    def readDefaultCalendarProperty(self, request, qname):
-        """
-        Read either the default VEVENT or VTODO calendar property. Try to pick one if not present.
-        """
-
-        tasks = qname == customxml.ScheduleDefaultTasksURL.qname()
-
-        # Must have a valid default
-        try:
-            defaultCalendarProperty = self.readDeadProperty(qname)
-        except HTTPError:
-            defaultCalendarProperty = None
-        if defaultCalendarProperty and len(defaultCalendarProperty.children) == 1:
-            defaultCalendar = str(defaultCalendarProperty.children[0])
-            cal = (yield request.locateResource(str(defaultCalendar)))
-            if cal is not None and isCalendarCollectionResource(cal) and cal.exists() and not cal.isShareeCollection():
-                returnValue(defaultCalendarProperty)
-
-        # Default is not valid - we have to try to pick one
-        defaultCalendarProperty = (yield self.pickNewDefaultCalendar(request, tasks=tasks))
-        returnValue(defaultCalendarProperty)
-
-
-    @inlineCallbacks
-    def writeDefaultCalendarProperty(self, request, property):
-        """
-        Write either the default VEVENT or VTODO calendar property, validating and canonicalizing the value
-        """
-        tasks = property.qname() == customxml.ScheduleDefaultTasksURL
-        componentType = "VTODO" if tasks else "VEVENT"
-        prop_to_set = customxml.ScheduleDefaultTasksURL if tasks else caldavxml.ScheduleDefaultCalendarURL
-        error_element = (calendarserver_namespace, "valid-schedule-default-tasks-URL") if tasks else (caldav_namespace, "valid-schedule-default-calendar-URL")
-
-        # Verify that the calendar added in the PROPPATCH is valid.
-        property.children = [davxml.HRef(normalizeURL(str(href))) for href in property.children]
-        new_calendar = [str(href) for href in property.children]
-        cal = None
-        if len(new_calendar) == 1:
-            calURI = str(new_calendar[0])
-            cal = (yield request.locateResource(str(new_calendar[0])))
-
-        # TODO: check that owner of the new calendar is the same as owner of this inbox
-        if cal is None or not cal.exists() or not isCalendarCollectionResource(cal) or \
-            cal.isShareeCollection() or not cal.isSupportedComponent(componentType):
-            # Validate that href's point to a valid calendar.
-            raise HTTPError(ErrorResponse(
-                responsecode.CONFLICT,
-                error_element,
-                "Invalid URI",
-            ))
-        else:
-            # Canonicalize the URL to __uids__ form
-            calURI = (yield cal.canonicalURL(request))
-            property = prop_to_set(davxml.HRef(calURI))
-            returnValue(property)
-
-
-    @inlineCallbacks
-    def pickNewDefaultCalendar(self, request, tasks=False):
-        """
-        First see if default provisioned calendar exists in the calendar home and pick that. Otherwise
-        pick another from the calendar home.
-        """
-
-        componentType = "VTODO" if tasks else "VEVENT"
-        test_name = "tasks" if tasks else "calendar"
-        prop_to_set = customxml.ScheduleDefaultTasksURL if tasks else caldavxml.ScheduleDefaultCalendarURL
-
-        calendarHomeURL = self.parent.url()
-        defaultCalendarURL = joinURL(calendarHomeURL, test_name)
-        defaultCalendar = (yield request.locateResource(defaultCalendarURL))
-        if defaultCalendar is None or not defaultCalendar.exists():
-            # Really, the dead property shouldn't be necessary, and this should
-            # be entirely computed by a back-end method like 'defaultCalendar()'
-
-            @inlineCallbacks
-            def _findDefault():
-                for calendarName in (yield self.parent._newStoreHome.listCalendars()):  # These are only unshared children
-                    if calendarName == "inbox":
-                        continue
-                    calendar = (yield self.parent._newStoreHome.calendarWithName(calendarName))
-                    if not calendar.owned():
-                        continue
-                    if not calendar.isSupportedComponent(componentType):
-                        continue
-                    break
-                else:
-                    calendarName = None
-                returnValue(calendarName)
-
-            foundName = yield _findDefault()
-            if foundName is None:
-                # Create a default and try and get its name again
-                yield self.parent._newStoreHome.ensureDefaultCalendarsExist()
-                foundName = yield _findDefault()
-                if foundName is None:
-                    # Failed to even create a default - bad news...
-                    raise RuntimeError("No valid calendars to use as a default %s calendar." % (componentType,))
-
-            defaultCalendarURL = joinURL(calendarHomeURL, foundName)
-
-        prop = prop_to_set(davxml.HRef(defaultCalendarURL))
-        self.writeDeadProperty(prop)
-        returnValue(prop)
-
-
-    @inlineCallbacks
-    def defaultCalendar(self, request, componentType):
-        """
-        Find the default calendar for the supplied iCalendar component type. If one does
-        not exist, automatically provision it.
-        """
-
-        # Check any default calendar property first - this will create if none exists
-        default = (yield self.readProperty(caldavxml.ScheduleDefaultCalendarURL.qname(), request))
-        if len(default.children) == 1:
-            defaultURL = str(default.children[0])
-            default = (yield request.locateResource(defaultURL))
-        else:
-            default = None
-
-        # Check that default handles the component type
-        if default is not None:
-            if not default.isSupportedComponent(componentType):
-                default = None
-
-        # Must have a default - provision one if not
-        if default is None:
-
-            # Try to find a calendar supporting the required component type. If there are multiple, pick
-            # the one with the oldest created timestamp as that will likely be the initial provision.
-            for calendarName in (yield self.parent._newStoreHome.listCalendars()):  # These are only unshared children
-                if calendarName == "inbox":
-                    continue
-                calendar = (yield self.parent._newStoreHome.calendarWithName(calendarName))
-                if not calendar.isSupportedComponent(componentType):
-                    continue
-                if default is None or calendar.created() < default.created():
-                    default = calendar
-
-            # If none can be found, provision one
-            if default is None:
-                new_name = "%ss" % (componentType.lower()[1:],)
-                default = yield self.parent._newStoreHome.createCalendarWithName(new_name)
-                yield default.setSupportedComponents(componentType.upper())
-
-            # Need L{DAVResource} object to return not new store object
-            default = (yield request.locateResource(joinURL(self.parent.url(), default.name())))
-
-        returnValue(default)
-
-
-    @inlineCallbacks
-    def isDefaultCalendar(self, request, calendar):
-        """
-        Is the supplied calendar one of the possible default calendars.
-        """
-        assert calendar.isCalendarCollection()
-
-        # Not allowed to delete the default calendar
-        for default_prop in (caldavxml.ScheduleDefaultCalendarURL, customxml.ScheduleDefaultTasksURL,):
-            default = (yield self.readProperty(default_prop.qname(), request))
-            if default and len(default.children) == 1:
-                defaultURL = normalizeURL(str(default.children[0]))
-                myURL = (yield calendar.canonicalURL(request))
-                if defaultURL == myURL:
-                    returnValue(default_prop)
-
-        returnValue(None)
-
-
-    ##
-    # ACL
-    ##
-
-    def supportedPrivileges(self, request):
-        return succeed(deliverSchedulePrivilegeSet)
-
-
-    def defaultAccessControlList(self):
-
-        privs = (
-            davxml.Privilege(caldavxml.ScheduleDeliver()),
-        )
-        if config.Scheduling.CalDAV.OldDraftCompatibility:
-            privs += (davxml.Privilege(caldavxml.Schedule()),)
-
-        return davxml.ACL(
-            # CalDAV:schedule-deliver for any authenticated user
-            davxml.ACE(
-                davxml.Principal(davxml.Authenticated()),
-                davxml.Grant(*privs),
-            ),
-        )
-
-
-
-class ScheduleOutboxResource (CalendarSchedulingCollectionResource):
-    """
-    CalDAV schedule Outbox resource.
-
-    Extends L{DAVResource} to provide CalDAV functionality.
-    """
-
-    def resourceType(self):
-        return davxml.ResourceType.scheduleOutbox
-
-
-    def getSupportedComponentSet(self):
-        return caldavxml.SupportedCalendarComponentSet(
-            *[caldavxml.CalendarComponent(name=item) for item in allowedComponents]
-        )
-
-
-    @inlineCallbacks
-    def http_POST(self, request):
-        """
-        The CalDAV POST method.
-
-        This uses a generator function yielding either L{waitForDeferred} objects or L{Response} objects.
-        This allows for code that follows a 'linear' execution pattern rather than having to use nested
-        L{Deferred} callbacks. The logic is easier to follow this way plus we don't run into deep nesting
-        issues which the other approach would have with large numbers of recipients.
-        """
-        # Check authentication and access controls
-        yield self.authorize(request, (caldavxml.ScheduleSend(),))
-
-        # This is a local CALDAV scheduling operation.
-        scheduler = CalDAVScheduler(request, self)
-
-        # Do the POST processing treating
-        result = (yield scheduler.doSchedulingViaPOST())
-        returnValue(result.response())
-
-
-    ##
-    # ACL
-    ##
-
-    def supportedPrivileges(self, request):
-        return succeed(sendSchedulePrivilegeSet)
-
-
-    def defaultAccessControlList(self):
-        if config.EnableProxyPrincipals:
-            myPrincipal = self.parent.principalForRecord()
-
-            privs = (
-                davxml.Privilege(caldavxml.ScheduleSend()),
-            )
-            if config.Scheduling.CalDAV.OldDraftCompatibility:
-                privs += (davxml.Privilege(caldavxml.Schedule()),)
-
-            return davxml.ACL(
-                # CalDAV:schedule for associated write proxies
-                davxml.ACE(
-                    davxml.Principal(davxml.HRef(joinURL(myPrincipal.principalURL(), "calendar-proxy-write"))),
-                    davxml.Grant(*privs),
-                    davxml.Protected(),
-                ),
-            )
-        else:
-            return super(ScheduleOutboxResource, self).defaultAccessControlList()
-
-
-    def report_urn_ietf_params_xml_ns_caldav_calendar_query(self, request, calendar_query):
-        return succeed(MultiStatusResponse(()))
-
-
-    def report_urn_ietf_params_xml_ns_caldav_calendar_multiget(self, request, multiget):
-        responses = [davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)) for href in multiget.resources]
-        return succeed(MultiStatusResponse((responses)))

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/scheduler.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/scheduler.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/scheduler.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -66,12 +66,12 @@
         self.doingPOST = False
 
 
-    def doSchedulingViaPOST(self, transaction):
+    def doSchedulingViaPOST(self):
         """
         The Scheduling POST operation on an Outbox.
         """
         self.doingPOST = True
-        return super(CalDAVScheduler, self).doSchedulingViaPOST(transaction)
+        return super(CalDAVScheduler, self).doSchedulingViaPOST()
 
 
     def checkAuthorization(self):
@@ -127,9 +127,9 @@
             else:
                 # Map recipient to their inbox
                 inbox = None
-                if principal.thisServer():
+                if principal.calendarsEnabled() and principal.thisServer():
                     if principal.locallyHosted():
-                        recipient_home = yield self.txn.calendarHomeWithUID(principal.uid)
+                        recipient_home = yield self.txn.calendarHomeWithUID(principal.uid, create=True)
                         if recipient_home:
                             inbox = (yield recipient_home.calendarWithName("inbox"))
                     else:

Deleted: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/test/test_resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/test/test_resource.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/caldav/test/test_resource.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -1,457 +0,0 @@
-##
-# Copyright (c) 2005-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 twext.web2 import responsecode, http_headers
-from twext.web2.dav.util import davXMLFromStream
-from twext.web2.http import HTTPError
-from twext.web2.iweb import IResponse
-from twext.web2.stream import MemoryStream
-from twext.web2.test.test_server import SimpleRequest
-from twisted.internet.defer import inlineCallbacks
-from twistedcaldav import caldavxml, customxml
-from twistedcaldav.test.util import HomeTestCase, TestCase
-from txdav.xml import element as davxml
-
-class Properties (HomeTestCase):
-    """
-    CalDAV properties
-    """
-    def test_free_busy_set_prop(self):
-        """
-        Test for PROPFIND on Inbox with missing calendar-free-busy-set property.
-        """
-
-        inbox_uri = "/inbox/"
-
-        def propfind_cb(response):
-            response = IResponse(response)
-
-            if response.code != responsecode.MULTI_STATUS:
-                self.fail("Incorrect response to PROPFIND: %s" % (response.code,))
-
-            def got_xml(doc):
-                if not isinstance(doc.root_element, davxml.MultiStatus):
-                    self.fail("PROPFIND response XML root element is not multistatus: %r" % (doc.root_element,))
-
-                response = doc.root_element.childOfType(davxml.Response)
-                href = response.childOfType(davxml.HRef)
-                self.failUnless(str(href) == inbox_uri)
-
-                for propstat in response.childrenOfType(davxml.PropertyStatus):
-                    status = propstat.childOfType(davxml.Status)
-                    if status.code != responsecode.OK:
-                        self.fail("Unable to read requested properties (%s): %r"
-                                  % (status, propstat.childOfType(davxml.PropertyContainer).toxml()))
-
-                container = propstat.childOfType(davxml.PropertyContainer)
-
-                #
-                # Check CalDAV:calendar-free-busy-set
-                #
-
-                free_busy_set = container.childOfType(caldavxml.CalendarFreeBusySet)
-                if not free_busy_set:
-                    self.fail("Expected CalDAV:calendar-free-busy-set element; but got none.")
-
-                if not free_busy_set.children:
-                    self.fail("Expected non-empty CalDAV:calendar-free-busy-set element.")
-
-            return davXMLFromStream(response.stream).addCallback(got_xml)
-
-        query = davxml.PropertyFind(
-                    davxml.PropertyContainer(
-                        caldavxml.CalendarFreeBusySet(),
-                    ),
-                )
-
-        request = SimpleRequest(
-            self.site,
-            "PROPFIND",
-            inbox_uri,
-            headers=http_headers.Headers({"Depth": "0"}),
-        )
-        request.stream = MemoryStream(query.toxml())
-        return self.send(request, propfind_cb)
-
-
-    @inlineCallbacks
-    def test_free_busy_set_remove_broken(self):
-        """
-        ???
-        """
-
-        request = SimpleRequest(self.site, "GET", "/inbox/")
-        inbox = yield request.locateResource("/inbox/")
-        self.assertTrue(inbox.hasDeadProperty(caldavxml.CalendarFreeBusySet))
-        oldfbset = set(("/calendar",))
-        oldset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in oldfbset])
-
-        newfbset = set()
-        newfbset.update(oldfbset)
-        newfbset.add("/calendar-broken")
-        newset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in newfbset])
-
-        inbox.writeDeadProperty(newset)
-        changedset = inbox.readDeadProperty(caldavxml.CalendarFreeBusySet)
-        self.assertEqual(tuple(changedset.children), tuple(newset.children))
-
-        yield inbox.writeProperty(newset, request)
-
-        changedset = inbox.readDeadProperty(caldavxml.CalendarFreeBusySet)
-        self.assertEqual(tuple(changedset.children), tuple(oldset.children))
-
-
-    @inlineCallbacks
-    def test_free_busy_set_strip_slash(self):
-        """
-        ???
-        """
-
-        request = SimpleRequest(self.site, "GET", "/inbox/")
-        inbox = yield request.locateResource("/inbox/")
-        self.assertTrue(inbox.hasDeadProperty(caldavxml.CalendarFreeBusySet))
-
-        oldfbset = set(("/calendar/",))
-        oldset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in oldfbset])
-        inbox.writeDeadProperty(oldset)
-
-        writefbset = set(("/calendar/",))
-        writeset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in writefbset])
-        yield inbox.writeProperty(writeset, request)
-
-        correctfbset = set(("/calendar",))
-        correctset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in correctfbset])
-        changedset = inbox.readDeadProperty(caldavxml.CalendarFreeBusySet)
-        self.assertEqual(tuple(changedset.children), tuple(correctset.children))
-
-
-    @inlineCallbacks
-    def test_free_busy_set_strip_slash_remove(self):
-        """
-        ???
-        """
-
-        request = SimpleRequest(self.site, "GET", "/inbox/")
-        inbox = yield request.locateResource("/inbox/")
-        self.assertTrue(inbox.hasDeadProperty(caldavxml.CalendarFreeBusySet))
-
-        oldfbset = set(("/calendar/", "/broken/"))
-        oldset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in oldfbset])
-        inbox.writeDeadProperty(oldset)
-
-        writefbset = set(("/calendar/", "/broken/"))
-        writeset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in writefbset])
-        yield inbox.writeProperty(writeset, request)
-
-        correctfbset = set(("/calendar",))
-        correctset = caldavxml.CalendarFreeBusySet(*[davxml.HRef(url) for url in correctfbset])
-        changedset = inbox.readDeadProperty(caldavxml.CalendarFreeBusySet)
-        self.assertEqual(tuple(changedset.children), tuple(correctset.children))
-
-
-
-class DefaultCalendar (TestCase):
-
-    def setUp(self):
-        super(DefaultCalendar, self).setUp()
-        self.createStockDirectoryService()
-        self.setupCalendars()
-
-
-    @inlineCallbacks
-    def test_pick_default_vevent_calendar(self):
-        """
-        Test that pickNewDefaultCalendar will choose the correct calendar.
-        """
-
-        request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
-        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
-
-        # default property initially not present
-        try:
-            inbox.readDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
-        except HTTPError:
-            pass
-        else:
-            self.fail("caldavxml.ScheduleDefaultCalendarURL is not empty")
-
-        yield inbox.pickNewDefaultCalendar(request)
-
-        try:
-            default = inbox.readDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
-        except HTTPError:
-            self.fail("caldavxml.ScheduleDefaultCalendarURL is not present")
-        else:
-            self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar")
-
-        request._newStoreTransaction.abort()
-
-
-    @inlineCallbacks
-    def test_pick_default_vtodo_calendar(self):
-        """
-        Test that pickNewDefaultCalendar will choose the correct tasks calendar.
-        """
-
-        request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
-        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
-
-        # default property initially not present
-        try:
-            inbox.readDeadProperty(customxml.ScheduleDefaultTasksURL)
-        except HTTPError:
-            pass
-        else:
-            self.fail("customxml.ScheduleDefaultTasksURL is not empty")
-
-        yield inbox.pickNewDefaultCalendar(request, tasks=True)
-
-        try:
-            default = inbox.readDeadProperty(customxml.ScheduleDefaultTasksURL)
-        except HTTPError:
-            self.fail("customxml.ScheduleDefaultTasksURL is not present")
-        else:
-            self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/tasks")
-
-        request._newStoreTransaction.abort()
-
-
-    @inlineCallbacks
-    def test_missing_default_vevent_calendar(self):
-        """
-        Test that pickNewDefaultCalendar will create a missing default calendar.
-        """
-
-        request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
-        home = yield request.locateResource("/calendars/users/wsanchez/")
-        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
-
-        # default property initially not present
-        try:
-            inbox.readDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
-        except HTTPError:
-            pass
-        else:
-            self.fail("caldavxml.ScheduleDefaultCalendarURL is not empty")
-
-        # Forcibly remove the one we need
-        yield home._newStoreHome.removeChildWithName("calendar")
-        names = [calendarName for calendarName in (yield home._newStoreHome.listCalendars())]
-        self.assertTrue("calendar" not in names)
-
-        yield inbox.pickNewDefaultCalendar(request)
-
-        try:
-            default = inbox.readDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
-        except HTTPError:
-            self.fail("caldavxml.ScheduleDefaultCalendarURL is not present")
-        else:
-            self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar")
-
-        request._newStoreTransaction.abort()
-
-
-    @inlineCallbacks
-    def test_missing_default_vtodo_calendar(self):
-        """
-        Test that pickNewDefaultCalendar will create a missing default tasks calendar.
-        """
-
-        request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
-        home = yield request.locateResource("/calendars/users/wsanchez/")
-        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
-
-        # default property initially not present
-        try:
-            inbox.readDeadProperty(customxml.ScheduleDefaultTasksURL)
-        except HTTPError:
-            pass
-        else:
-            self.fail("caldavxml.ScheduleDefaultTasksURL is not empty")
-
-        # Forcibly remove the one we need
-        yield home._newStoreHome.removeChildWithName("tasks")
-        names = [calendarName for calendarName in (yield home._newStoreHome.listCalendars())]
-        self.assertTrue("tasks" not in names)
-
-        yield inbox.pickNewDefaultCalendar(request, tasks=True)
-
-        try:
-            default = inbox.readDeadProperty(customxml.ScheduleDefaultTasksURL)
-        except HTTPError:
-            self.fail("caldavxml.ScheduleDefaultTasksURL is not present")
-        else:
-            self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/tasks")
-
-        request._newStoreTransaction.abort()
-
-
-    @inlineCallbacks
-    def test_pick_default_other(self):
-        """
-        Make calendar
-        """
-
-        request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
-        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
-
-        # default property not present
-        try:
-            inbox.readDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
-        except HTTPError:
-            pass
-        else:
-            self.fail("caldavxml.ScheduleDefaultCalendarURL is not empty")
-
-        # Create a new default calendar
-        newcalendar = yield request.locateResource("/calendars/users/wsanchez/newcalendar")
-        yield newcalendar.createCalendarCollection()
-        inbox.writeDeadProperty(caldavxml.ScheduleDefaultCalendarURL(
-            davxml.HRef("/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")
-        ))
-
-        # Delete the normal calendar
-        calendar = yield request.locateResource("/calendars/users/wsanchez/calendar")
-        yield calendar.storeRemove(request, False, "/calendars/users/wsanchez/calendar")
-
-        inbox.removeDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
-
-        # default property not present
-        try:
-            inbox.readDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
-        except HTTPError:
-            pass
-        else:
-            self.fail("caldavxml.ScheduleDefaultCalendarURL is not empty")
-        request._newStoreTransaction.commit()
-
-        request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
-        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
-        yield inbox.pickNewDefaultCalendar(request)
-
-        try:
-            default = inbox.readDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
-        except HTTPError:
-            self.fail("caldavxml.ScheduleDefaultCalendarURL is not present")
-        else:
-            self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")
-
-        request._newStoreTransaction.abort()
-
-
-    @inlineCallbacks
-    def test_fix_shared_default(self):
-        """
-        Make calendar
-        """
-
-        request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
-        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
-
-        # Create a new default calendar
-        newcalendar = yield request.locateResource("/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")
-        yield newcalendar.createCalendarCollection()
-        inbox.writeDeadProperty(caldavxml.ScheduleDefaultCalendarURL(
-            davxml.HRef("/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")
-        ))
-        try:
-            default = yield inbox.readProperty(caldavxml.ScheduleDefaultCalendarURL, request)
-        except HTTPError:
-            self.fail("caldavxml.ScheduleDefaultCalendarURL is not present")
-        else:
-            self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")
-
-        # Force the new calendar to think it is a virtual share
-        newcalendar._isShareeCollection = True
-
-        try:
-            default = yield inbox.readProperty(caldavxml.ScheduleDefaultCalendarURL, request)
-        except HTTPError:
-            self.fail("caldavxml.ScheduleDefaultCalendarURL is not present")
-        else:
-            self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/calendar")
-
-        request._newStoreTransaction.abort()
-
-
-    @inlineCallbacks
-    def test_set_default_vevent_other(self):
-        """
-        Test that the default URL can be set to another VEVENT calendar
-        """
-
-        request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
-        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
-
-        # default property not present
-        try:
-            inbox.readDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
-        except HTTPError:
-            pass
-        else:
-            self.fail("caldavxml.ScheduleDefaultCalendarURL is not empty")
-
-        # Create a new default calendar
-        newcalendar = yield request.locateResource("/calendars/users/wsanchez/newcalendar")
-        yield newcalendar.createCalendarCollection()
-        yield newcalendar.setSupportedComponents(("VEVENT",))
-        request._newStoreTransaction.commit()
-
-        request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
-        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
-        yield inbox.writeProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef("/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")), request)
-
-        try:
-            default = inbox.readDeadProperty(caldavxml.ScheduleDefaultCalendarURL)
-        except HTTPError:
-            self.fail("caldavxml.ScheduleDefaultCalendarURL is not present")
-        else:
-            self.assertEqual(str(default.children[0]), "/calendars/__uids__/6423F94A-6B76-4A3A-815B-D52CFD77935D/newcalendar")
-
-        request._newStoreTransaction.commit()
-
-
-    @inlineCallbacks
-    def test_is_default_calendar(self):
-        """
-        Test .isDefaultCalendar() returns the proper class or None.
-        """
-
-        # Create a new non-default calendar
-        request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
-        newcalendar = yield request.locateResource("/calendars/users/wsanchez/newcalendar")
-        yield newcalendar.createCalendarCollection()
-        yield newcalendar.setSupportedComponents(("VEVENT",))
-        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
-        yield inbox.pickNewDefaultCalendar(request)
-        request._newStoreTransaction.commit()
-
-        request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
-        inbox = yield request.locateResource("/calendars/users/wsanchez/inbox")
-        calendar = yield request.locateResource("/calendars/users/wsanchez/calendar")
-        newcalendar = yield request.locateResource("/calendars/users/wsanchez/newcalendar")
-        tasks = yield request.locateResource("/calendars/users/wsanchez/tasks")
-
-        result = yield inbox.isDefaultCalendar(request, calendar)
-        self.assertEqual(result, caldavxml.ScheduleDefaultCalendarURL)
-
-        result = yield inbox.isDefaultCalendar(request, newcalendar)
-        self.assertEqual(result, None)
-
-        result = yield inbox.isDefaultCalendar(request, tasks)
-        self.assertEqual(result, customxml.ScheduleDefaultTasksURL)
-
-        request._newStoreTransaction.commit()

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/scheduler.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/scheduler.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/scheduling/scheduler.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -17,9 +17,10 @@
 from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.python.failure import Failure
 
+from twext.enterprise.locking import NamedLock
 from twext.python.log import Logger, LoggingMixIn
 from twext.web2 import responsecode
-from twext.web2.http import HTTPError, Response, StatusResponse
+from twext.web2.http import HTTPError, Response
 from twext.web2.http_headers import MimeType
 from txdav.xml import element as davxml
 from twext.web2.dav.http import messageForFailure, statusForFailure, \
@@ -30,7 +31,6 @@
 from twistedcaldav.accounting import accountingEnabled, emitAccounting
 from twistedcaldav.config import config
 from twistedcaldav.ical import Component
-from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
 from txdav.caldav.datastore.scheduling import addressmapping
 from txdav.caldav.datastore.scheduling.caldav.delivery import ScheduleViaCalDAV
 from txdav.caldav.datastore.scheduling.cuaddress import InvalidCalendarUser, \
@@ -42,6 +42,7 @@
 from txdav.caldav.datastore.scheduling.imip.delivery import ScheduleViaIMip
 from txdav.caldav.datastore.scheduling.ischedule.delivery import ScheduleViaISchedule
 from txdav.caldav.datastore.scheduling.itip import iTIPRequestStatus
+import hashlib
 
 """
 CalDAV/Server-to-Server scheduling behavior.
@@ -153,49 +154,25 @@
 
 
     @inlineCallbacks
-    def doSchedulingViaPOST(self, use_request_headers=False):
+    def doSchedulingViaPOST(self, originator, recipients, calendar):
         """
         The Scheduling POST operation on an Outbox.
         """
 
-        self.method = "POST"
+        self.calendar = calendar
+        self.preProcessCalendarData()
 
-        # Load various useful bits doing some basic checks on those
-        yield self.loadCalendarFromRequest()
-
-        if use_request_headers:
-            self.loadFromRequestHeaders()
-        else:
-            yield self.loadFromRequestData()
-
         if self.logItems is not None:
-            self.logItems["recipients"] = len(self.recipients)
-            self.logItems["cl"] = str(len(str(self.calendar)))
+            self.logItems["recipients"] = len(recipients)
+            self.logItems["cl"] = str(len(str(calendar)))
 
-        # Do some extra authorization checks
-        self.checkAuthorization()
-
         # We might trigger an implicit scheduling operation here that will require consistency
         # of data for all events with the same UID. So detect this and use a lock
-        if self.calendar.resourceType() != "VFREEBUSY":
-            uid = self.calendar.resourceUID()
-            lock = MemcacheLock(
-                "ImplicitUIDLock",
-                uid,
-                timeout=config.Scheduling.Options.UIDLockTimeoutSeconds,
-                expire_time=config.Scheduling.Options.UIDLockExpirySeconds,
-            )
+        if calendar.resourceType() != "VFREEBUSY":
+            uid = calendar.resourceUID()
+            yield NamedLock.acquire(self._txn, "ImplicitUIDLock:%s" % (hashlib.md5(uid).hexdigest(),))
 
-            try:
-                yield lock.acquire()
-            except MemcacheLockTimeoutError:
-                raise HTTPError(StatusResponse(responsecode.CONFLICT, "UID: %s currently in use on the server." % (uid,)))
-            else:
-                # Release lock after commit or abort
-                self.txn.postCommit(lock.clean)
-                self.txn.postAbort(lock.clean)
-
-        result = (yield self.doScheduling())
+        result = (yield self.doSchedulingDirectly("POST", originator, recipients, calendar))
         returnValue(result)
 
 
@@ -705,9 +682,9 @@
             else:
                 # Map recipient to their inbox
                 inbox = None
-                if principal.thisServer():
+                if principal.calendarsEnabled() and principal.thisServer():
                     if principal.locallyHosted():
-                        recipient_home = yield self.txn.calendarHomeWithUID(principal.uid)
+                        recipient_home = yield self.txn.calendarHomeWithUID(principal.uid, create=True)
                         if recipient_home:
                             inbox = (yield recipient_home.calendarWithName("inbox"))
                     else:

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -72,7 +72,7 @@
     InvalidUIDError, UIDExistsError, ResourceDeletedError, \
     AttendeeAllowedError, InvalidPerUserDataMerge, ComponentUpdateState, \
     ValidOrganizerError, ShareeAllowedError, ComponentRemoveState, \
-    InvalidComponentForStoreError, InvalidResourceMove
+    InvalidComponentForStoreError, InvalidResourceMove, InvalidDefaultCalendar
 from txdav.caldav.icalendarstore import QuotaExceeded
 from txdav.common.datastore.sql import CommonHome, CommonHomeChild, \
     CommonObjectResource, ECALENDARTYPE
@@ -727,6 +727,39 @@
 
 
     @inlineCallbacks
+    def setDefaultCalendar(self, calendar, tasks=False):
+        """
+        Set the default calendar for a particular type of component.
+
+        @param calendar: the calendar being set as the default
+        @type calendar: L{CalendarObject}
+        @param tasks: C{True} for VTODO, C{False} for VEVENT
+        @type componentType: C{bool}
+        """
+        chm = self._homeMetaDataSchema
+        componentType = "VTODO" if tasks else "VEVENT"
+        attribute_to_test = "_default_tasks" if tasks else "_default_events"
+        column_to_set = chm.DEFAULT_TASKS if tasks else chm.DEFAULT_EVENTS
+
+        # Check validity of the default
+        if calendar.isInbox():
+            raise InvalidDefaultCalendar("Cannot set inbox as a default calendar")
+        elif not calendar.owned():
+            raise InvalidDefaultCalendar("Cannot set shared calendar as a default calendar")
+        elif not calendar.isSupportedComponent(componentType):
+            raise InvalidDefaultCalendar("Cannot set default calendar to unsupported component type")
+        elif calendar.ownerHome().uid() != self.uid():
+            raise InvalidDefaultCalendar("Cannot set default calendar to someone else's calendar")
+
+        setattr(self, attribute_to_test, calendar._resourceID)
+        yield Update(
+            {column_to_set: calendar._resourceID},
+            Where=chm.RESOURCE_ID == self._resourceID,
+        ).on(self._txn)
+        yield self.invalidateQueryCache()
+
+
+    @inlineCallbacks
     def defaultCalendar(self, componentType):
         """
         Find the default calendar for the supplied iCalendar component type. If one does
@@ -741,10 +774,14 @@
         else:
             default = None
 
-        # Check that default handles the component type
+        # Check that default meets requirements
         if default is not None:
-            if not default.isSupportedComponent(componentType):
+            if default.isInbox():
                 default = None
+            elif not default.owned():
+                default = None
+            elif not default.isSupportedComponent(componentType):
+                default = None
 
         # Must have a default - provision one if not
         if default is None:
@@ -755,9 +792,9 @@
                 calendar = (yield self.calendarWithName(calendarName))
                 if calendar.isInbox():
                     continue
-                if not calendar.owned():
+                elif not calendar.owned():
                     continue
-                if not calendar.isSupportedComponent(componentType):
+                elif not calendar.isSupportedComponent(componentType):
                     continue
                 if default is None or calendar.created() < default.created():
                     default = calendar

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/common.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/common.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -275,9 +275,10 @@
         },
         "home_defaults": {
             "calendar_1": {
-                "1.ics": (cal1NoSplitsRoot.child("1.ics").getContent(), metadata1),
-                "3.ics": (cal1NoSplitsRoot.child("3.ics").getContent(), metadata3),
+                "1.ics": (cal1DefaultsRoot.child("1.ics").getContent(), metadata1),
+                "3.ics": (cal1DefaultsRoot.child("3.ics").getContent(), metadata3),
             },
+            "inbox" : {},
         },
     }
     md5s = {

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_sql.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/test/test_sql.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -14,7 +14,8 @@
 # limitations under the License.
 ##
 from txdav.caldav.datastore.test.util import buildCalendarStore
-from txdav.caldav.icalendarstore import ComponentUpdateState
+from txdav.caldav.icalendarstore import ComponentUpdateState, \
+    InvalidDefaultCalendar
 
 """
 Tests for txdav.caldav.datastore.postgres, mostly based on
@@ -1163,6 +1164,54 @@
 
 
     @inlineCallbacks
+    def test_setDefaultCalendar(self):
+        """
+        Make sure a default_events calendar is assigned.
+        """
+
+        home = yield self.homeUnderTest(name="home_defaults")
+        calendar1 = yield home.calendarWithName("calendar_1")
+        yield calendar1.splitCollectionByComponentTypes()
+        yield self.commit()
+
+        home = yield self.homeUnderTest(name="home_defaults")
+        self.assertEqual(home._default_events, None)
+        self.assertEqual(home._default_tasks, None)
+        calendar1 = yield home.calendarWithName("calendar_1")
+        yield home.setDefaultCalendar(calendar1, False)
+        self.assertEqual(home._default_events, calendar1._resourceID)
+        self.assertEqual(home._default_tasks, None)
+        yield self.commit()
+
+        home = yield self.homeUnderTest(name="home_defaults")
+        calendar1 = yield home.calendarWithName("calendar_1")
+        calendar2 = yield home.calendarWithName("calendar_1-vtodo")
+        yield self.failUnlessFailure(home.setDefaultCalendar(calendar2, False), InvalidDefaultCalendar)
+        self.assertEqual(home._default_events, calendar1._resourceID)
+        self.assertEqual(home._default_tasks, None)
+        yield self.commit()
+
+        home = yield self.homeUnderTest(name="home_defaults")
+        calendar1 = yield home.calendarWithName("calendar_1")
+        calendar2 = yield home.calendarWithName("calendar_1-vtodo")
+        yield home.setDefaultCalendar(calendar2, True)
+        self.assertEqual(home._default_events, calendar1._resourceID)
+        self.assertEqual(home._default_tasks, calendar2._resourceID)
+        yield self.commit()
+
+        home = yield self.homeUnderTest(name="home_defaults")
+        calendar1 = yield home.calendarWithName("inbox")
+        yield self.failUnlessFailure(home.setDefaultCalendar(calendar1, False), InvalidDefaultCalendar)
+        yield self.commit()
+
+        home = yield self.homeUnderTest(name="home_defaults")
+        home_other = yield self.homeUnderTest(name="home_splits")
+        calendar1 = yield home_other.calendarWithName("calendar_1")
+        yield self.failUnlessFailure(home.setDefaultCalendar(calendar1, False), InvalidDefaultCalendar)
+        yield self.commit()
+
+
+    @inlineCallbacks
     def test_resourceLock(self):
         """
         Test CommonObjectResource.lock to make sure it locks, raises on missing resource,

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -206,6 +206,13 @@
 
 
 
+class InvalidDefaultCalendar(CommonStoreError):
+    """
+    Setting a default calendar failed.
+    """
+
+
+
 class AttachmentStoreFailed(Exception):
     """
     Unable to store an attachment.

Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/test/util.py	2013-04-17 23:02:28 UTC (rev 11056)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/test/util.py	2013-04-18 18:21:54 UTC (rev 11057)
@@ -598,18 +598,18 @@
     savedStore = None
     assertProvides = assertProvides
 
-    def transactionUnderTest(self):
+    def transactionUnderTest(self, txn=None):
         """
         Create a transaction from C{storeUnderTest} and save it as
         C{lastTransaction}.  Also makes sure to use the same store, saving the
         value from C{storeUnderTest}.
         """
         if self.lastTransaction is None:
-            self.lastTransaction = self.concurrentTransaction()
+            self.lastTransaction = self.concurrentTransaction(txn)
         return self.lastTransaction
 
 
-    def concurrentTransaction(self):
+    def concurrentTransaction(self, txn=None):
         """
         Create a transaction from C{storeUnderTest} and save it for later
         clean-up.
@@ -617,9 +617,12 @@
         if self.savedStore is None:
             self.savedStore = self.storeUnderTest()
         self.counter += 1
-        txn = self.savedStore.newTransaction(
-            self.id() + " #" + str(self.counter)
-        )
+        if txn is None:
+            txn = self.savedStore.newTransaction(
+                self.id() + " #" + str(self.counter)
+            )
+        else:
+            txn._label = self.id() + " #" + str(self.counter)
         @inlineCallbacks
         def maybeCommitThis():
             try:
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130418/67051836/attachment-0001.html>


More information about the calendarserver-changes mailing list