[CalendarServer-changes] [4170] CalendarServer/branches/release/CalendarServer-2.2-dev

source_changes at macosforge.org source_changes at macosforge.org
Mon May 4 18:13:52 PDT 2009


Revision: 4170
          http://trac.macosforge.org/projects/calendarserver/changeset/4170
Author:   wsanchez at apple.com
Date:     2009-05-04 18:13:51 -0700 (Mon, 04 May 2009)
Log Message:
-----------
Pulled up r4143 r4144 r4159 from trunk.

Modified Paths:
--------------
    CalendarServer/branches/release/CalendarServer-2.2-dev/memcacheclient.py
    CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/memcacheprops.py
    CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/test/util.py

Added Paths:
-----------
    CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/test/test_memcacheprops.py

Property Changed:
----------------
    CalendarServer/branches/release/CalendarServer-2.2-dev/
    CalendarServer/branches/release/CalendarServer-2.2-dev/doc/Extensions/caldav-privatecomments.txt
    CalendarServer/branches/release/CalendarServer-2.2-dev/doc/Extensions/caldav-privatecomments.xml
    CalendarServer/branches/release/CalendarServer-2.2-dev/doc/Extensions/caldav-schedulingchanges.txt
    CalendarServer/branches/release/CalendarServer-2.2-dev/doc/Extensions/caldav-schedulingchanges.xml


Property changes on: CalendarServer/branches/release/CalendarServer-2.2-dev
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/trunk:4105-4107,4113-4116,4121-4122,4124,4137-4139,4141-4142,4156-4158,4163-4165
   + /CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/trunk:4105-4107,4113-4116,4121-4122,4124,4137-4139,4141-4144,4156-4159,4163-4165


Property changes on: CalendarServer/branches/release/CalendarServer-2.2-dev/doc/Extensions/caldav-privatecomments.txt
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-privatecomments-00.txt:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-privatecomments-00.txt:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-privatecomments.txt:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-privatecomments-00.txt:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-privatecomments.txt:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-privatecomments.txt:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-privatecomments.txt:4068-4075
/CalendarServer/trunk/doc/Extensions/caldav-privatecomments.txt:4105-4107,4113-4116,4121-4122,4124,4137-4139,4141-4142,4156-4158,4163-4165
   + /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-privatecomments-00.txt:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-privatecomments-00.txt:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-privatecomments.txt:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-privatecomments-00.txt:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-privatecomments.txt:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-privatecomments.txt:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-privatecomments.txt:4068-4075
/CalendarServer/trunk/doc/Extensions/caldav-privatecomments.txt:4105-4107,4113-4116,4121-4122,4124,4137-4139,4141-4144,4156-4159,4163-4165


Property changes on: CalendarServer/branches/release/CalendarServer-2.2-dev/doc/Extensions/caldav-privatecomments.xml
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-privatecomments-00.xml:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-privatecomments-00.xml:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-privatecomments.xml:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-privatecomments-00.xml:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-privatecomments.xml:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-privatecomments.xml:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-privatecomments.xml:4068-4075
/CalendarServer/trunk/doc/Extensions/caldav-privatecomments.xml:4105-4107,4113-4116,4121-4122,4124,4137-4139,4141-4142,4156-4158,4163-4165
   + /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-privatecomments-00.xml:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-privatecomments-00.xml:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-privatecomments.xml:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-privatecomments-00.xml:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-privatecomments.xml:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-privatecomments.xml:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-privatecomments.xml:4068-4075
/CalendarServer/trunk/doc/Extensions/caldav-privatecomments.xml:4105-4107,4113-4116,4121-4122,4124,4137-4139,4141-4144,4156-4159,4163-4165


Property changes on: CalendarServer/branches/release/CalendarServer-2.2-dev/doc/Extensions/caldav-schedulingchanges.txt
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-schedulingchanges-01.txt:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-schedulingchanges-01.txt:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-schedulingchanges.txt:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-schedulingchanges-01.txt:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-schedulingchanges.txt:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-schedulingchanges.txt:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-schedulingchanges.txt:4068-4075
/CalendarServer/trunk/doc/Extensions/caldav-schedulingchanges.txt:4105-4107,4113-4116,4121-4122,4124,4137-4139,4141-4142,4156-4158,4163-4165
   + /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-schedulingchanges-01.txt:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-schedulingchanges-01.txt:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-schedulingchanges.txt:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-schedulingchanges-01.txt:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-schedulingchanges.txt:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-schedulingchanges.txt:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-schedulingchanges.txt:4068-4075
/CalendarServer/trunk/doc/Extensions/caldav-schedulingchanges.txt:4105-4107,4113-4116,4121-4122,4124,4137-4139,4141-4144,4156-4159,4163-4165


Property changes on: CalendarServer/branches/release/CalendarServer-2.2-dev/doc/Extensions/caldav-schedulingchanges.xml
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-schedulingchanges-01.xml:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-schedulingchanges-01.xml:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-schedulingchanges.xml:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-schedulingchanges-01.xml:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-schedulingchanges.xml:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-schedulingchanges.xml:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-schedulingchanges.xml:4068-4075
/CalendarServer/trunk/doc/Extensions/caldav-schedulingchanges.xml:4105-4107,4113-4116,4121-4122,4124,4137-4139,4141-4142,4156-4158,4163-4165
   + /CalendarServer/branches/users/cdaboo/attendee-comments-2886/doc/Extensions/caldav-schedulingchanges-01.xml:2887-2910
/CalendarServer/branches/users/cdaboo/byebye-serviceslocator-2937/doc/Extensions/caldav-schedulingchanges-01.xml:2938-3097
/CalendarServer/branches/users/cdaboo/implicit-if-match-3306/doc/Extensions/caldav-schedulingchanges.xml:3307-3349
/CalendarServer/branches/users/cdaboo/implicitauto-2947/doc/Extensions/caldav-schedulingchanges-01.xml:2948-2989
/CalendarServer/branches/users/cdaboo/location-partial-accept-3573/doc/Extensions/caldav-schedulingchanges.xml:3574-3581
/CalendarServer/branches/users/sagen/resource-delegates-4038/doc/Extensions/caldav-schedulingchanges.xml:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/doc/Extensions/caldav-schedulingchanges.xml:4068-4075
/CalendarServer/trunk/doc/Extensions/caldav-schedulingchanges.xml:4105-4107,4113-4116,4121-4122,4124,4137-4139,4141-4144,4156-4159,4163-4165

Modified: CalendarServer/branches/release/CalendarServer-2.2-dev/memcacheclient.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-2.2-dev/memcacheclient.py	2009-05-05 01:12:44 UTC (rev 4169)
+++ CalendarServer/branches/release/CalendarServer-2.2-dev/memcacheclient.py	2009-05-05 01:13:51 UTC (rev 4170)
@@ -50,7 +50,11 @@
 import re
 import types
 from twistedcaldav.config import config
+from twistedcaldav.log import Logger
 
+log = Logger()
+
+
 try:
     import cPickle as pickle
 except ImportError:
@@ -97,6 +101,11 @@
     NOT_FOUND error
     """
 
+class TokenMismatchError(MemcacheError):
+    """
+    Check-and-set token mismatch
+    """
+
 try:
     # Only exists in Python 2.4+
     from threading import local
@@ -311,6 +320,7 @@
                 #print "(using server %s)" % server,
                 return server, key
             serverhash = serverHashFunction(str(serverhash) + str(i))
+        log.error("Memcacheclient _get_server( ) failed to connect")
         return None, None
 
     def disconnect_all(self):
@@ -714,16 +724,29 @@
         if not store_info: return(0)
 
         if token is not None:
+            cmd = "cas"
             fullcmd = "cas %s %d %d %d %s\r\n%s" % (key, store_info[0], time, store_info[1], token, store_info[2])
         else:
             fullcmd = "%s %s %d %d %d\r\n%s" % (cmd, key, store_info[0], time, store_info[1], store_info[2])
         try:
             server.send_cmd(fullcmd)
             result = server.expect("STORED")
+
+            if (result == "STORED"):
+                return True
+
             if (result == "NOT_FOUND"):
                 raise NotFoundError(key)
-            return (result == "STORED")
 
+            if token and result == "EXISTS":
+                log.error("Memcacheclient check-and-set failed")
+                raise TokenMismatchError(key)
+
+            log.error("Memcacheclient %s command failed with result (%s)" %
+                (cmd, result))
+
+            return False
+
         except socket.error, msg:
             if type(msg) is types.TupleType: msg = msg[1]
             server.mark_dead(msg)
@@ -1055,7 +1078,7 @@
             if self.data.has_key(key):
                 stored_val, stored_token = self.data[key]
                 if token != stored_token:
-                    return False
+                    raise TokenMismatchError(key)
 
         self.data[key] = (serialized, str(self.token))
         self.token += 1
@@ -1152,12 +1175,14 @@
         return 0
 
     def mark_dead(self, reason):
+        log.error("Memcacheclient socket marked dead (%s)" % (reason,))
         self.debuglog("MemCache: %s: %s.  Marking dead." % (self, reason))
         self.deaduntil = time.time() + _Host._DEAD_RETRY
         self.close_socket()
 
     def _get_socket(self):
         if self._check_dead():
+            log.error("Memcacheclient _get_socket() found dead socket")
             return None
         if self.socket:
             return self.socket
@@ -1166,10 +1191,14 @@
         try:
             s.connect(self.address)
         except socket.timeout, msg:
+            log.error("Memcacheclient _get_socket() connection timed out (%s)" %
+                (msg,))
             self.mark_dead("connect: %s" % msg)
             return None
         except socket.error, msg:
             if type(msg) is types.TupleType: msg = msg[1]
+            log.error("Memcacheclient _get_socket() connection error (%s)" %
+                (msg,))
             self.mark_dead("connect: %s" % msg[1])
             return None
         self.socket = s

Modified: CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/memcacheprops.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/memcacheprops.py	2009-05-05 01:12:44 UTC (rev 4169)
+++ CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/memcacheprops.py	2009-05-05 01:13:51 UTC (rev 4170)
@@ -32,7 +32,7 @@
 except ImportError:
     from md5 import new as md5
 
-from memcacheclient import ClientFactory as MemcacheClientFactory, MemcacheError
+from memcacheclient import ClientFactory as MemcacheClientFactory, MemcacheError, TokenMismatchError
 
 from twisted.python.filepath import FilePath
 from twisted.web2 import responsecode
@@ -59,6 +59,7 @@
         if not hasattr(MemcachePropertyCollection, "_memcacheClient"):
 
             log.info("Instantiating memcache connection for MemcachePropertyCollection")
+
             MemcachePropertyCollection._memcacheClient = MemcacheClientFactory.getClient(["%s:%s" % (config.Memcached.BindAddress, config.Memcached.Port)],
                 debug=0,
                 pickleProtocol=2,
@@ -198,25 +199,57 @@
 
         return cache
 
-    def setProperty(self, child, property):
+    def setProperty(self, child, property, delete=False):
         propertyCache, key, childCache, token = self.childCache(child)
 
-        if childCache.get(property.qname(), None) == property:
-            # No changes
-            return
+        if delete:
+            qname = property
+            if childCache.has_key(qname):
+                del childCache[qname]
+        else:
+            qname = property.qname()
+            childCache[qname] = property
 
-        childCache[property.qname()] = property
+        client = self.memcacheClient()
 
-        client = self.memcacheClient()
         if client is not None:
-            result = client.set(key, childCache, time=self.cacheTimeout, token=token)
-            if not result:
+            retries = 10
+            while retries:
+                try:
+                    if client.set(key, childCache, time=self.cacheTimeout,
+                        token=token):
+                        # Success
+                        break
+
+                except TokenMismatchError:
+                    # The value in memcache has changed since we last
+                    # fetched it
+                    log.debug("memcacheprops setProperty TokenMismatchError; retrying...")
+
+                finally:
+                    # Re-fetch the properties for this child
+                    loaded = self._loadCache(childNames=(child.fp.basename(),))
+                    propertyCache.update(loaded.iteritems())
+
+                retries -= 1
+
+                propertyCache, key, childCache, token = self.childCache(child)
+
+                if delete:
+                    if childCache.has_key(qname):
+                        del childCache[qname]
+                else:
+                    childCache[qname] = property
+
+            else:
+                log.error("memcacheprops setProperty had too many failures")
                 delattr(self, "_propertyCache")
-                raise MemcacheError("Unable to set property %s on %s"
-                                    % (property.sname(), child))
+                raise MemcacheError("Unable to %s property {%s}%s on %s"
+                    % ("delete" if delete else "set",
+                    qname[0], qname[1], child))
 
-            loaded = self._loadCache(childNames=(child.fp.basename(),))
-            propertyCache.update(loaded.iteritems())
+    def deleteProperty(self, child, qname):
+        return self.setProperty(child, qname, delete=True)
 
     def flushCache(self, child):
         path = child.fp.path
@@ -232,22 +265,6 @@
             if not result:
                 raise MemcacheError("Unable to flush cache on %s" % (child,))
 
-    def deleteProperty(self, child, qname):
-        propertyCache, key, childCache, token = self.childCache(child)
-
-        del childCache[qname]
-
-        client = self.memcacheClient()
-        if client is not None:
-            result = client.set(key, childCache, time=self.cacheTimeout, token=token)
-            if not result:
-                delattr(self, "_propertyCache")
-                raise MemcacheError("Unable to delete property {%s}%s on %s"
-                                    % (qname[0], qname[1], child))
-
-            loaded = self._loadCache(childNames=(child.fp.basename(),))
-            propertyCache.update(loaded.iteritems())
-
     def propertyStoreForChild(self, child, childPropertyStore):
         return self.ChildPropertyStore(self, child, childPropertyStore)
 

Copied: CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/test/test_memcacheprops.py (from rev 4159, CalendarServer/trunk/twistedcaldav/test/test_memcacheprops.py)
===================================================================
--- CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/test/test_memcacheprops.py	                        (rev 0)
+++ CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/test/test_memcacheprops.py	2009-05-05 01:13:51 UTC (rev 4170)
@@ -0,0 +1,194 @@
+##
+# Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# 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
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+##
+
+"""
+Test memcacheprops.
+"""
+
+import os
+
+from twisted.internet.defer import inlineCallbacks
+from twisted.web2.http import HTTPError
+
+from twistedcaldav.config import config
+from twistedcaldav.memcacheprops import MemcachePropertyCollection
+from twistedcaldav.test.util import InMemoryPropertyStore
+from twistedcaldav.test.util import TestCase
+
+
+
+class StubCollection(object):
+
+    def __init__(self, path, childNames):
+        self.path = path
+        self.fp = StubFP(path)
+        self.children = {}
+
+        for childName in childNames:
+            self.children[childName] = StubResource(self, path, childName)
+
+    def listChildren(self):
+        return self.children.iterkeys()
+
+    def getChild(self, childName):
+        return self.children[childName]
+
+    def propertyCollection(self):
+        if not hasattr(self, "_propertyCollection"):
+            self._propertyCollection = MemcachePropertyCollection(self)
+        return self._propertyCollection
+
+
+class StubResource(object):
+
+    def __init__(self, parent, path, name):
+        self.parent = parent
+        self.fp = StubFP(os.path.join(path, name))
+
+    def deadProperties(self):
+        if not hasattr(self, "_dead_properties"):
+            self._dead_properties = self.parent.propertyCollection().propertyStoreForChild(self, InMemoryPropertyStore())
+        return self._dead_properties
+
+class StubFP(object):
+
+    def __init__(self, path):
+        self.path = path
+
+    def child(self, childName):
+        class _Child(object):
+            def __init__(self, path):
+                self.path = path
+        return _Child(os.path.join(self.path, childName))
+
+    def basename(self):
+        return os.path.basename(self.path)
+
+class StubProperty(object):
+
+    def __init__(self, ns, name, value=None):
+        self.ns = ns
+        self.name = name
+        self.value = value
+
+    def qname(self):
+        return self.ns, self.name
+
+
+    def __repr__(self):
+        return "{%s}%s = %s" % (self.ns, self.name, self.value)
+
+
+class MemcachePropertyCollectionTestCase(TestCase):
+    """
+    Test MemcacheProprtyCollection
+    """
+
+    def getColl(self):
+        return StubCollection("calendars", ["a", "b", "c"])
+
+    # @inlineCallbacks
+    def test_setget(self):
+
+        child1 = self.getColl().getChild("a")
+        child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val1"))
+
+        child2 = self.getColl().getChild("a")
+        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+            "val1")
+
+        child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val2"))
+
+        # force memcache to be consulted (once per collection per request)
+        child1 = self.getColl().getChild("a")
+
+        self.assertEquals(child1.deadProperties().get(("ns1:", "prop1")).value,
+            "val2")
+
+    def test_merge(self):
+        child1 = self.getColl().getChild("a")
+        child2 = self.getColl().getChild("a")
+        child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val0"))
+        child1.deadProperties().set(StubProperty("ns1:", "prop2", value="val0"))
+        child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val0"))
+
+        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+            "val0")
+        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+            "val0")
+        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+            "val0")
+
+        child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val1"))
+        child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val3"))
+
+        # force memcache to be consulted (once per collection per request)
+        child2 = self.getColl().getChild("a")
+
+        # verify properties
+        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+            "val1")
+        self.assertEquals(child2.deadProperties().get(("ns1:", "prop2")).value,
+            "val0")
+        self.assertEquals(child2.deadProperties().get(("ns1:", "prop3")).value,
+            "val3")
+
+        self.assertEquals(child1.deadProperties().get(("ns1:", "prop1")).value,
+            "val1")
+        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+            "val0")
+        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+            "val3")
+
+    def test_delete(self):
+        child1 = self.getColl().getChild("a")
+        child2 = self.getColl().getChild("a")
+        child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val0"))
+        child1.deadProperties().set(StubProperty("ns1:", "prop2", value="val0"))
+        child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val0"))
+
+        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+            "val0")
+        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+            "val0")
+        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+            "val0")
+
+        child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val1"))
+        child1.deadProperties().delete(("ns1:", "prop1"))
+        self.assertRaises(HTTPError, child1.deadProperties().get, ("ns1:", "prop1"))
+
+        self.assertFalse(child1.deadProperties().contains(("ns1:", "prop1"))) 
+        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+            "val0")
+        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+            "val0")
+
+        # force memcache to be consulted (once per collection per request)
+        child2 = self.getColl().getChild("a")
+
+        # verify properties
+        self.assertFalse(child2.deadProperties().contains(("ns1:", "prop1"))) 
+        self.assertEquals(child2.deadProperties().get(("ns1:", "prop2")).value,
+            "val0")
+        self.assertEquals(child2.deadProperties().get(("ns1:", "prop3")).value,
+            "val0")

Modified: CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/test/util.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/test/util.py	2009-05-05 01:12:44 UTC (rev 4169)
+++ CalendarServer/branches/release/CalendarServer-2.2-dev/twistedcaldav/test/util.py	2009-05-05 01:13:51 UTC (rev 4170)
@@ -158,8 +158,17 @@
     def set(self, property):
         self._properties[property.qname()] = property
 
+    def delete(self, qname):
+        try:
+            del self._properties[qname]
+        except KeyError:
+            pass
 
 
+    def list(self):
+        return self._properties.iterkeys()
+
+
 class StubCacheChangeNotifier(object):
     def __init__(self, *args, **kwargs):
         pass
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090504/000a9442/attachment-0001.html>


More information about the calendarserver-changes mailing list