[CalendarServer-changes] [2317] CalendarServer/branches/propfind-cache

source_changes at macosforge.org source_changes at macosforge.org
Tue Apr 15 17:27:59 PDT 2008


Revision: 2317
          http://trac.macosforge.org/projects/calendarserver/changeset/2317
Author:   dreid at apple.com
Date:     2008-04-15 17:27:58 -0700 (Tue, 15 Apr 2008)

Log Message:
-----------
Decouple changed notifications from resource location, by implementing a specialized Notifier class.

Modified Paths:
--------------
    CalendarServer/branches/propfind-cache/test
    CalendarServer/branches/propfind-cache/twistedcaldav/resource.py
    CalendarServer/branches/propfind-cache/twistedcaldav/static.py

Added Paths:
-----------
    CalendarServer/branches/propfind-cache/twistedcaldav/cache.py
    CalendarServer/branches/propfind-cache/twistedcaldav/test/test_cache.py

Removed Paths:
-------------
    CalendarServer/branches/propfind-cache/twistedcaldav/test/test_resource.py
    CalendarServer/branches/propfind-cache/twistedcaldav/test/test_static.py

Modified: CalendarServer/branches/propfind-cache/test
===================================================================
--- CalendarServer/branches/propfind-cache/test	2008-04-16 00:25:50 UTC (rev 2316)
+++ CalendarServer/branches/propfind-cache/test	2008-04-16 00:27:58 UTC (rev 2317)
@@ -24,6 +24,7 @@
     random="--random=$(date "+%s")";
  no_colour="";
 until_fail="";
+  coverage="";
 
 usage ()
 {
@@ -37,18 +38,20 @@
   echo "        -n  Do not use color";
   echo "        -o  Do not run tests in random order.";
   echo "        -u  Run until the tests fail.";
+  echo "        -c  Generate coverage reports.";
 
   if [ "${1-}" == "-" ]; then return 0; fi;
   exit 64;
 }
 
-while getopts "nhou" option; do
+while getopts "nhouc" option; do
   case "${option}" in
     '?') usage; ;;
     'h') usage -; exit 0; ;;
     'o')     random=""; ;;
     'n')  no_colour="--reporter=bwverbose"; ;;
     'u') until_fail="--until-failure"; ;;
+    'c')   coverage="--coverage"; ;;
   esac;
 done;
 shift $((${OPTIND} - 1));
@@ -64,4 +67,4 @@
     test_modules="twistedcaldav twisted";
 fi;
 
-cd "${wd}" && "${twisted}/bin/trial" ${random} ${until_fail} ${no_colour} --coverage ${test_modules};
+cd "${wd}" && "${twisted}/bin/trial" ${random} ${until_fail} ${no_colour} ${coverage} ${test_modules};

Added: CalendarServer/branches/propfind-cache/twistedcaldav/cache.py
===================================================================
--- CalendarServer/branches/propfind-cache/twistedcaldav/cache.py	                        (rev 0)
+++ CalendarServer/branches/propfind-cache/twistedcaldav/cache.py	2008-04-16 00:27:58 UTC (rev 2317)
@@ -0,0 +1,68 @@
+##
+# Copyright (c) 2008 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+import uuid
+
+
+from twisted.web2.dav import davxml
+from twisted.web2.http import HTTPError
+
+
+class CacheTokensProperty(davxml.WebDAVTextElement):
+    namespace = davxml.twisted_private_namespace
+    name = "cacheTokens"
+
+
+class CacheChangeNotifier(object):
+    def __init__(self, propertyStore):
+        self._propertyStore = propertyStore
+        self._propToken = None
+        self._dataToken = None
+
+    def _newDataToken(self):
+        return uuid.uuid4()
+
+    _newPropertyToken = _newDataToken
+
+    def _loadTokens(self):
+        try:
+            tokens = self._propertyStore.get(CacheTokensProperty.qname())
+            self._propToken, self._dataToken = (
+                tokens.children[0].data.split(':'))
+
+        except HTTPError, e:
+            pass
+
+    def _writeTokens(self):
+        if self._propToken is None:
+            self._propToken = self._newPropertyToken()
+
+        if self._dataToken is None:
+            self._dataToken = self._newDataToken()
+
+        property = CacheTokensProperty.fromString('%s:%s' % (self._propToken,
+                                                             self._dataToken))
+        self._propertyStore.set(property)
+
+    def propertiesChanged(self):
+        self._loadTokens()
+        self._propToken = self._newPropertyToken()
+        self._writeTokens()
+
+    def dataChanged(self):
+        self._loadTokens()
+        self._dataToken = self._newDataToken()
+        self._writeTokens()

Modified: CalendarServer/branches/propfind-cache/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/propfind-cache/twistedcaldav/resource.py	2008-04-16 00:25:50 UTC (rev 2316)
+++ CalendarServer/branches/propfind-cache/twistedcaldav/resource.py	2008-04-16 00:27:58 UTC (rev 2317)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2008 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2007 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.
@@ -156,7 +156,7 @@
                 d = self.owner(request)
                 d.addCallback(lambda x: davxml.Owner(x))
                 return d
-
+            
         elif namespace == caldav_namespace:
             if name == "supported-calendar-component-set":
                 # CalDAV-access-09, section 5.2.3
@@ -195,7 +195,7 @@
                         responsecode.NOT_IMPLEMENTED,
                         "Component %s is not supported by this server" % (component.toxml(),)
                     ))
-
+                    
         # Strictly speaking CalDAV:timezone 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
@@ -313,7 +313,7 @@
         owner = d.getResult()
         result = (davxml.Principal(owner) == self.currentPrincipal(request))
         yield result
-
+ 
     ##
     # CalDAV
     ##
@@ -351,13 +351,13 @@
 
         def checkPrivilegesError(failure):
             failure.trap(AccessDeniedError)
-
+            
             reactor.callLater(0, getChild)
 
         def checkPrivileges(child):
             if privileges is None:
                 return child
-
+   
             ca = child.checkPrivileges(request, privileges)
             ca.addCallback(lambda ign: child)
             return ca
@@ -366,7 +366,7 @@
             if child.isCalendarCollection():
                 callback(child, childpath)
             elif child.isCollection():
-                if depth == "infinity":
+                if depth == "infinity": 
                     fc = child.findCalendarCollections(depth, request, callback, privileges)
                     fc.addCallback(lambda x: reactor.callLater(0, getChild))
                     return fc
@@ -478,10 +478,10 @@
         calendar collection have the same privileges unless explicitly overridden. The same applies
         to drop box collections as we want all resources (attachments) to have the same privileges as
         the drop box collection.
-
+        
         @param newaces: C{list} of L{ACE} for ACL being set.
         """
-
+        
         # Do this only for regular calendar collections and Inbox/Outbox
         if self.isPseudoCalendarCollection():
             edited_aces = []
@@ -494,7 +494,7 @@
                     edited_aces.append(ace)
         else:
             edited_aces = newaces
-
+        
         # Do inherited with possibly modified set of aces
         super(CalDAVResource, self).writeNewACEs(edited_aces)
 
@@ -507,38 +507,9 @@
         Locates the parent resource of the resource with the given URI.
         @param request: an L{IRequest} object for the request being processed.
         @param uri: the URI whose parent resource is desired.
-
-        @return: A L{Deferred} that fires with an L{IResource} provider that is
-            the parent of the current URL.
         """
         return request.locateResource(parentForURL(uri))
 
-
-    def changed(self, request, uri, properties=False, data=False):
-        """
-        Notifies that this resource has changed either it's properties or it's
-        data.
-
-        @param request: an L{IRequest} object for the request being processed.
-        @param uri: The URI that has changed.
-
-        @param properties: A C{bool} indicating that properties on C{uri} have
-            changed.
-        @param data: A C{bool} indicating that data at C{uri} has changed.
-
-        @return: A L{Deferred} that fires with None.
-        """
-        def _gotParentResource(resource):
-            if resource is not None:
-                return resource.changed(request,
-                                        uri,
-                                        properties=properties,
-                                        data=data)
-        d = self.locateParent(request, request.urlForResource(self))
-        d.addCallback(_gotParentResource)
-        return d
-
-
 class CalendarPrincipalCollectionResource (DAVPrincipalCollectionResource, CalDAVResource):
     """
     CalDAV principal collection.
@@ -704,14 +675,14 @@
             def getFreeBusy(has):
                 if not has:
                     return ()
-
+    
                 def parseFreeBusy(freeBusySet):
                     return tuple(str(href) for href in freeBusySet.children)
-
+        
                 d = inbox.readProperty((caldav_namespace, "calendar-free-busy-set"), request)
                 d.addCallback(parseFreeBusy)
                 return d
-
+    
             d = inbox.hasProperty((caldav_namespace, "calendar-free-busy-set"), request)
             d.addCallback(getFreeBusy)
             return d
@@ -739,10 +710,10 @@
         """
         if self.hasDeadProperty((caldav_namespace, "schedule-outbox-URL")):
             outbox = self.readDeadProperty((caldav_namespace, "schedule-outbox-URL"))
-            return str(outbox.children[0])
+            return str(outbox.children[0])        
         else:
             return None
-
+        
     def dropboxURL(self):
         """
         @return: the drop box home collection URL for this principal.
@@ -752,7 +723,7 @@
             return str(inbox.children[0])
         else:
             return None
-
+        
     def notificationsURL(self):
         """
         @return: the notifications collection URL for this principal.

Modified: CalendarServer/branches/propfind-cache/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/propfind-cache/twistedcaldav/static.py	2008-04-16 00:25:50 UTC (rev 2316)
+++ CalendarServer/branches/propfind-cache/twistedcaldav/static.py	2008-04-16 00:27:58 UTC (rev 2317)
@@ -37,7 +37,6 @@
 import datetime
 import os
 import errno
-import uuid
 from urlparse import urlsplit
 
 from twisted.internet.defer import deferredGenerator, fail, succeed, waitForDeferred
@@ -45,7 +44,6 @@
 from twisted.web2 import responsecode
 from twisted.web2.http import HTTPError, StatusResponse
 from twisted.web2.dav import davxml
-
 from twisted.web2.dav.fileop import mkcollection, rmdir
 from twisted.web2.dav.http import ErrorResponse
 from twisted.web2.dav.idav import IDAVResource
@@ -119,30 +117,28 @@
                     responsecode.FORBIDDEN,
                     (caldavxml.caldav_namespace, "calendar-collection-location-ok")
                 ))
-
-            return self.createCalendarCollection(request)
-
+    
+            return self.createCalendarCollection()
+            
         parent = self._checkParents(request, isPseudoCalendarCollectionResource)
         parent.addCallback(_defer)
         return parent
 
-    def createCalendarCollection(self, request):
+    def createCalendarCollection(self):
         #
         # Create the collection once we know it is safe to do so
         #
         def onCalendarCollection(status):
             if status != responsecode.CREATED:
                 raise HTTPError(status)
-
+    
             # Initialize CTag on the calendar collection
-            d2 = self.changed(request,
-                              request.urlForResource(self),
-                              data=True)
-
+            self.updateCTag()
+            
             # Create the index so its ready when the first PUTs come in
-            d2.addCallback(lambda ign: self.index().create())
-            d2.addCallback(lambda ign: status)
-            return d2
+            self.index().create()
+            
+            return status
 
         d = self.createSpecialCollection(davxml.ResourceType.calendar)
         d.addCallback(onCalendarCollection)
@@ -155,10 +151,10 @@
         def onCollection(status):
             if status != responsecode.CREATED:
                 raise HTTPError(status)
-
+    
             self.writeDeadProperty(resourceType)
             return status
-
+        
         def onError(f):
             try:
                 rmdir(self.fp)
@@ -171,7 +167,7 @@
             d.addCallback(onCollection)
         d.addErrback(onError)
         return d
-
+ 
     def iCalendarRolledup(self, request):
         if self.isPseudoCalendarCollection():
             # Generate a monolithic calendar
@@ -193,7 +189,7 @@
                     child = IDAVResource(child)
                 except TypeError:
                     child = None
-
+    
                 if child is not None:
                     # Check privileges of child - skip if access denied
                     try:
@@ -207,7 +203,7 @@
 
                     for component in subcalendar.subcomponents():
                         calendar.addComponent(component)
-
+                        
             yield calendar
             return
 
@@ -260,7 +256,7 @@
             d = self.locateParent(request, request.urlForResource(self))
             d.addCallback(gotParent)
             return d
-
+        
         return super(CalDAVFile, self).supportedPrivileges(request)
 
     ##
@@ -290,16 +286,6 @@
         assert self.isCollection()
         self.writeDeadProperty(customxml.GETCTag(str(datetime.datetime.now())))
 
-    def changed(self, request, uri, properties=False, data=False):
-        if self.isCollection() and data:
-            self.updateCTag()
-
-        return super(CalDAVFile, self).changed(
-            request,
-            uri,
-            properties=properties,
-            data=data)
-
     ##
     # Quota
     ##
@@ -317,17 +303,17 @@
                 """
                 Recursively descend the directory tree rooted at top,
                 calling the callback function for each regular file
-
+                
                 @param top: L{FilePath} for the directory to walk.
                 """
-
+            
                 total = 0
                 for f in top.listdir():
-
+    
                     # Ignore the database
                     if f.startswith("."):
                         continue
-
+    
                     child = top.child(f)
                     if child.isdir():
                         # It's a directory, recurse into it
@@ -340,11 +326,11 @@
                     else:
                         # Unknown file type, print a message
                         pass
-
+            
                 yield total
-
+            
             walktree = deferredGenerator(walktree)
-
+    
             return walktree(self.fp)
         else:
             return succeed(self.fp.getsize())
@@ -368,20 +354,20 @@
         #
         # Parse the URI
         #
-
+    
         (scheme, host, path, query, fragment) = urlsplit(uri) #@UnusedVariable
-
+    
         # Request hostname and child uri hostname have to be the same.
         if host and host != request.headers.getHeader("host"):
             return False
-
+        
         # Child URI must start with request uri text.
         parent = request.uri
         if not parent.endswith("/"):
             parent += "/"
-
+            
         return path.startswith(parent) and (len(path) > len(parent)) and (not immediateChild or (path.find("/", len(parent)) == -1))
-
+    
     def _checkParents(self, request, test):
         """
         @param request: the request being processed.
@@ -406,7 +392,7 @@
                 return
 
         yield None
-
+    
     _checkParents = deferredGenerator(_checkParents)
 
 class AutoProvisioningFileMixIn (AutoProvisioningResourceMixIn):
@@ -427,7 +413,7 @@
             parent = self.parent
             if not parent.exists() and isinstance(parent, AutoProvisioningFileMixIn):
                 parent.provision()
-
+                
             assert parent.exists(), "Parent %s of %s does not exist" % (parent, self)
             assert parent.isCollection(), "Parent %s of %s is not a collection" % (parent, self)
 
@@ -448,7 +434,7 @@
 
 class CalendarHomeProvisioningFile (AutoProvisioningFileMixIn, DirectoryCalendarHomeProvisioningResource, DAVFile):
     """
-    Resource which provisions calendar home collections as needed.
+    Resource which provisions calendar home collections as needed.    
     """
     def __init__(self, path, directory, url):
         """
@@ -550,7 +536,7 @@
             NotificationsCollectionFileClass = NotificationsCollectionFile
         else:
             NotificationsCollectionFileClass = None
-
+            
         cls = {
             "inbox"        : ScheduleInboxFile,
             "outbox"       : ScheduleOutboxFile,
@@ -576,29 +562,6 @@
 
         return super(CalendarHomeFile, self).getChild(name)
 
-
-    def _newCacheToken(self, property=False, data=False):
-        return uuid.uuid4()
-
-    def changed(self, request, uri, properties=False, data=False):
-        try:
-            oldCacheTokens = self.readDeadProperty(CacheTokensProperty.qname())
-            propToken, dataToken = oldCacheTokens.children[0].data.split(':')
-
-            if properties is True:
-                propToken = self._newCacheToken(property=True)
-
-            if data is True:
-                dataToken = self._newCacheToken(data=True)
-
-        except HTTPError, e:
-            propToken, dataToken = (self._newCacheToken(property=True),
-                                    self._newCacheToken(data=True))
-
-        self.writeDeadProperty(
-            CacheTokensProperty.fromString('%s:%s' % (propToken, dataToken)))
-        return succeed(None)
-
 class ScheduleFile (AutoProvisioningFileMixIn, CalDAVFile):
     def __init__(self, path, parent):
         super(ScheduleFile, self).__init__(path, principalCollections=parent.principalCollections())
@@ -758,9 +721,9 @@
         elements = []
         elements.append(customxml.TimeStamp.fromString(timestamp))
         elements.append(customxml.Changed(davxml.HRef.fromString(parentURL)))
-
+                          
         xml = customxml.Notification(*elements)
-
+        
         d = waitForDeferred(put_common_base.storeResource(request, data=xml.toxml(), destination=self, destination_uri=request.urlForResource(self)))
         yield d
         d.getResult()
@@ -856,13 +819,6 @@
 
 calendarPrivilegeSet = _calendarPrivilegeSet()
 
-
-class CacheTokensProperty(davxml.WebDAVTextElement):
-    namespace = davxml.twisted_private_namespace
-    name = "cacheTokens"
-
-davxml.registerElement(CacheTokensProperty)
-
 ##
 # Attach methods
 ##

Added: CalendarServer/branches/propfind-cache/twistedcaldav/test/test_cache.py
===================================================================
--- CalendarServer/branches/propfind-cache/twistedcaldav/test/test_cache.py	                        (rev 0)
+++ CalendarServer/branches/propfind-cache/twistedcaldav/test/test_cache.py	2008-04-16 00:27:58 UTC (rev 2317)
@@ -0,0 +1,87 @@
+##
+# Copyright (c) 2008 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 new import instancemethod
+
+from twisted.trial.unittest import TestCase
+
+from twisted.web2.http import HTTPError, StatusResponse
+
+from twistedcaldav.cache import CacheChangeNotifier, CacheTokensProperty
+
+
+class InMemoryPropertyStore(object):
+    def __init__(self):
+        self._properties = {}
+
+    def get(self, qname):
+        data = self._properties.get(qname)
+        if data is None:
+            raise HTTPError(StatusResponse(404, "No such property"))
+        return data
+
+    def set(self, property):
+        self._properties[property.qname()] = property
+
+
+def _newCacheTokens(prefix):
+    def _(self):
+        called = getattr(self, '_%scalled' % (prefix,), 0)
+
+        token = '%sToken%d' % (prefix, called)
+        setattr(self, '_%scalled' % (prefix,), called + 1)
+        return token
+
+    return _
+
+
+class CacheChangeNotifierTests(TestCase):
+    def setUp(self):
+        self.props = InMemoryPropertyStore()
+        self.ccn = CacheChangeNotifier(self.props)
+        self.ccn._newPropertyToken = instancemethod(_newCacheTokens('prop'),
+                                                    self.ccn,
+                                                    CacheChangeNotifier)
+        self.ccn._newDataToken = instancemethod(_newCacheTokens('data'),
+                                                self.ccn,
+                                                CacheChangeNotifier)
+
+    def test_unsetPropertiesAreProvisioned(self):
+        self.ccn.propertiesChanged()
+        tokens = self.props._properties[CacheTokensProperty.qname()
+                                        ].children[0].data
+        self.assertEquals(
+            tokens,
+            'propToken0:dataToken0')
+
+    def test_changedPropertiesChangesPropToken(self):
+        self.ccn.propertiesChanged()
+        self.ccn.propertiesChanged()
+        tokens = self.props._properties[CacheTokensProperty.qname()
+                                        ].children[0].data
+        self.assertEquals(
+            tokens,
+            'propToken1:dataToken0')
+
+
+    def test_changedDataChangesDataToken(self):
+        self.ccn.dataChanged()
+        self.ccn.dataChanged()
+        tokens = self.props._properties[CacheTokensProperty.qname()
+                                        ].children[0].data
+        self.assertEquals(
+            tokens,
+            'propToken0:dataToken1')

Deleted: CalendarServer/branches/propfind-cache/twistedcaldav/test/test_resource.py
===================================================================
--- CalendarServer/branches/propfind-cache/twistedcaldav/test/test_resource.py	2008-04-16 00:25:50 UTC (rev 2316)
+++ CalendarServer/branches/propfind-cache/twistedcaldav/test/test_resource.py	2008-04-16 00:27:58 UTC (rev 2317)
@@ -1,119 +0,0 @@
-##
-# Copyright (c) 2008 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.
-##
-
-"""
-Unittests for CalDAV-aware resources found in twistedcaldav.resources
-"""
-
-from twisted.trial.unittest import TestCase
-from twistedcaldav.resource import CalDAVResource
-
-from twisted.internet.defer import succeed
-
-class StubLocatingRequest(object):
-    """
-    A stub request that only knows how to locate resources for uris.
-    """
-
-    def __init__(self, resources=None):
-        self.resources = resources or {}
-        self.urls = {}
-        self._loadUrls()
-
-    def _loadUrls(self):
-        for k, v in self.resources.iteritems():
-            self.urls[v] = k
-
-    def locateResource(self, url):
-        return succeed(self.resources.get(url))
-
-    def urlForResource(self, resrc):
-        return self.urls.get(resrc)
-
-
-class StubParentResource(object):
-    """
-    A stub resource that records it's arguments to the changed method.
-    """
-    changedArgs = None
-    changedKwArgs = None
-
-    def changed(self, *args, **kwargs):
-        self.changedArgs = args
-        self.changedKwArgs = kwargs
-        return succeed(None)
-
-
-class ChangedNotificationTestCase(TestCase):
-    """
-    Test that CalDAVResource's changed implementation propogates change
-    notifications to it's parent.
-    """
-    def setUp(self):
-        self.calendarHome = StubParentResource()
-        self.myCalendar = CalDAVResource()
-        self.myCalendarURI = '/calendars/users/dreid/calendar'
-
-        self.request = StubLocatingRequest({
-                '/calendars/users/dreid/': self.calendarHome,
-                self.myCalendarURI: self.myCalendar
-                })
-
-
-    def _test(self, changedArgs, changedKwargs,
-              expectedArgs, expectedKwargs):
-        def _ranTest(result):
-            self.assertEquals(self.calendarHome.changedArgs,
-                              expectedArgs)
-            self.assertEquals(self.calendarHome.changedKwArgs,
-                              expectedKwargs)
-
-        d = self.myCalendar.changed(*changedArgs, **changedKwargs)
-        d.addCallback(_ranTest)
-        return d
-
-    def test_notifiesParentResource(self):
-        return self._test((self.request, self.myCalendarURI),
-                          {},
-                          (self.request, self.myCalendarURI),
-                          {'properties': False, 'data': False})
-
-    def test_propertiesChanged(self):
-        return self._test((self.request, self.myCalendarURI),
-                          {'properties': True},
-                          (self.request, self.myCalendarURI),
-                          {'properties': True, 'data': False})
-
-    def test_dataChanged(self):
-        return self._test((self.request, self.myCalendarURI),
-                          {'data': True},
-                          (self.request, self.myCalendarURI),
-                          {'properties': False, 'data': True})
-
-    def test_changedOnRootResource(self):
-        self.request.resources = {'/': self.myCalendar}
-        self.request._loadUrls()
-        d = self.myCalendar.changed(self.request, '/', data=True)
-        return d
-
-    def test_deepChanged(self):
-        self.request.resources['/calendars/users/dreid/'] = CalDAVResource()
-        self.request.resources['/calendars/users/'] = CalDAVResource()
-        self.request.resources['/calendars/'] = CalDAVResource()
-        self.request.resources['/'] = CalDAVResource()
-        self.request._loadUrls()
-        d = self.myCalendar.changed(self.request, self.myCalendarURI, data=True)
-        return d

Deleted: CalendarServer/branches/propfind-cache/twistedcaldav/test/test_static.py
===================================================================
--- CalendarServer/branches/propfind-cache/twistedcaldav/test/test_static.py	2008-04-16 00:25:50 UTC (rev 2316)
+++ CalendarServer/branches/propfind-cache/twistedcaldav/test/test_static.py	2008-04-16 00:27:58 UTC (rev 2317)
@@ -1,205 +0,0 @@
-##
-# Copyright (c) 2008 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.
-##
-
-"""
-Unittests for CalDAV-aware static resources found in twistedcaldav.static
-"""
-from new import instancemethod
-
-from twisted.trial.unittest import TestCase
-from twistedcaldav.test.test_resource import StubLocatingRequest
-from twistedcaldav.test.test_resource import StubParentResource
-
-from twistedcaldav.static import CalendarHomeFile
-from twistedcaldav.static import CalDAVFile
-from twistedcaldav.static import CacheTokensProperty
-from twistedcaldav.customxml import GETCTag
-
-from twisted.web2.http import HTTPError, StatusResponse
-
-
-class InMemoryPropertyStore(object):
-    def __init__(self, resource):
-        self.resource = resource
-        self._properties = {}
-
-    def get(self, qname):
-        data = self._properties.get(qname)
-        if data is None:
-            raise HTTPError(StatusResponse(404, "No such property"))
-        return data
-
-    def set(self, property):
-        self._properties[property.qname()] = property
-
-
-def _newCacheTokenStub(self, property=False, data=False):
-    if not hasattr(self, '_called'):
-        self._called = [0,0]
-
-    if property:
-        token = 'propertyToken%d' % (self._called[0],)
-        self._called[0] += 1
-    elif data:
-        token = 'dataToken%d' % (self._called[1],)
-        self._called[1] += 1
-
-    return token
-
-
-class CalendarHomeChangedTests(TestCase):
-    """
-    Ensure that CalendarHomeFile's changed method updates the cache tokens when
-    called.
-    """
-    def setUp(self):
-        self.parent = StubParentResource()
-        self.parent.principalCollections = (lambda: [])
-        self.request = StubLocatingRequest({'/calendars/users/': self.parent})
-
-        self.myCalendarHome = CalendarHomeFile(self.mktemp(),
-                                               self.parent,
-                                               object())
-        self.properties = InMemoryPropertyStore(self.myCalendarHome)
-        self.myCalendarHome._dead_properties = self.properties
-        self.properties._properties[CacheTokensProperty.qname()] = (
-            CacheTokensProperty.fromString('propertyToken0:dataToken0'))
-
-        self.myCalendarHome._newCacheToken = instancemethod(
-            _newCacheTokenStub,
-            self.myCalendarHome,
-            CalendarHomeFile)
-
-        self.myCalendarHome._newCacheToken(property=True)
-        self.myCalendarHome._newCacheToken(data=True)
-
-
-    def test_doesntPropogateToParent(self):
-        def _checkChanged(result):
-            self.assertEquals(self.parent.changedArgs, None)
-            self.assertEquals(self.parent.changedKwArgs, None)
-            self.assertEquals(result, None)
-
-        d = self.myCalendarHome.changed(self.request, '/calendars/users/dreid')
-        d.addCallback(_checkChanged)
-        return d
-
-    def _test(self, properties=False, data=False, expectedTokens=None):
-        def _checkChanged(result):
-            tokens = self.properties._properties[CacheTokensProperty.qname()]
-            self.assertEquals(tokens.children[0].data, expectedTokens)
-            self.assertEquals(result, None)
-
-        d = self.myCalendarHome.changed(self.request, '/calendars/users/dreid',
-                                    properties=properties, data=data)
-        d.addCallback(_checkChanged)
-        return d
-
-    def test_changesPropertyToken(self):
-        return self._test(properties=True,
-                          expectedTokens='propertyToken1:dataToken0')
-
-
-    def test_changesDataToken(self):
-        return self._test(data=True, expectedTokens='propertyToken0:dataToken1')
-
-
-    def test_changesBothTokens(self):
-        return self._test(properties=True, data=True,
-                   expectedTokens='propertyToken1:dataToken1')
-
-
-    def test_initializesNonExistantProperty(self):
-        self.properties._properties = {}
-        self.myCalendarHome._called = [0,0]
-        return self._test(properties=False, data=False,
-                          expectedTokens='propertyToken0:dataToken0')
-
-
-class CalDAVFileChangedTests(TestCase):
-    """
-    Ensure that CalDAVFile's changed method updates the ctag on collections
-    and delegates to the parent.
-    """
-    def setUp(self):
-        self.parent = StubParentResource()
-        self.parent.principalCollections = (lambda: [])
-
-        self.myCalDAVFile = CalDAVFile(self.mktemp())
-        self.properties = InMemoryPropertyStore(self.myCalDAVFile)
-        self.myCalDAVFile._dead_properties = self.properties
-
-        self.request = StubLocatingRequest({
-                '/calendars/users/': self.parent,
-                '/calendars/users/dreid': self.myCalDAVFile
-                })
-
-
-    def test_propogatesToParent(self):
-        def _checkParentChanged(result):
-            self.assertEquals(self.parent.changedArgs,
-                              (self.request, '/calendars/users/dreid'))
-            self.assertEquals(self.parent.changedKwArgs,
-                              {'properties': False, 'data': False})
-
-        d = self.myCalDAVFile.changed(self.request, '/calendars/users/dreid')
-        d.addCallback(_checkParentChanged)
-        return d
-
-
-    def test_updatesCTagOnCollection(self):
-        def _checkCTag(result):
-            ctag = self.properties._properties.get(GETCTag.qname())
-            self.failUnless(isinstance(ctag, GETCTag))
-
-        self.myCalDAVFile.isCollection = (lambda: True)
-        d = self.myCalDAVFile.changed(self.request, '/calendars/users/dreid',
-                                      data=True)
-        d.addCallback(_checkCTag)
-        return d
-
-
-    def test_doesNotUpdateCTagOnNonCollection(self):
-        def _checkCTag(result):
-            ctag = self.properties._properties.get(GETCTag.qname())
-            self.assertEquals(ctag, None)
-
-        self.myCalDAVFile.isCollection = (lambda: False)
-        d = self.myCalDAVFile.changed(self.request, '/calendars/users/dreid')
-        return d
-
-
-    def test_doesNotUpdateCTagOnPropertyChange(self):
-        def _checkCTag(result):
-            ctag = self.properties._properties.get(GETCTag.qname())
-            self.assertEquals(ctag, None)
-
-        self.myCalDAVFile.isCollection = (lambda: True)
-        d = self.myCalDAVFile.changed(self.request, '/calendars/users/dreid',
-                                      properties=True)
-        return d
-
-
-    def test_createCalendarCollectionCallsChanged(self):
-        def _checkChangedOnParent(result):
-            self.assertEquals(self.parent.changedArgs,
-                              (self.request, '/calendars/users/dreid'))
-            self.assertEquals(self.parent.changedKwArgs,
-                              {'properties': False, 'data': True})
-
-        d = self.myCalDAVFile.createCalendarCollection(self.request)
-        d.addCallback(_checkChangedOnParent)
-        return d

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080415/9afdd2c4/attachment-0001.html


More information about the calendarserver-changes mailing list