[CalendarServer-changes] [3831] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Mar 11 16:34:26 PDT 2009


Revision: 3831
          http://trac.macosforge.org/projects/calendarserver/changeset/3831
Author:   wsanchez at apple.com
Date:     2009-03-11 16:34:25 -0700 (Wed, 11 Mar 2009)
Log Message:
-----------
Move memcache module to twext.protocols

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/memcachepool.py
    CalendarServer/trunk/twistedcaldav/test/test_memcachelock.py

Added Paths:
-----------
    CalendarServer/trunk/twext/protocols/
    CalendarServer/trunk/twext/protocols/__init__.py
    CalendarServer/trunk/twext/protocols/memcache.py
    CalendarServer/trunk/twext/protocols/test/
    CalendarServer/trunk/twext/protocols/test/__init__.py
    CalendarServer/trunk/twext/protocols/test/test_memcache.py

Removed Paths:
-------------
    CalendarServer/trunk/twistedcaldav/memcache.py
    CalendarServer/trunk/twistedcaldav/test/test_memcache.py

Added: CalendarServer/trunk/twext/protocols/__init__.py
===================================================================
--- CalendarServer/trunk/twext/protocols/__init__.py	                        (rev 0)
+++ CalendarServer/trunk/twext/protocols/__init__.py	2009-03-11 23:34:25 UTC (rev 3831)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2009 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.
+##
+
+"""
+Extentions to twisted.protocols
+"""

Copied: CalendarServer/trunk/twext/protocols/memcache.py (from rev 3828, CalendarServer/trunk/twistedcaldav/memcache.py)
===================================================================
--- CalendarServer/trunk/twext/protocols/memcache.py	                        (rev 0)
+++ CalendarServer/trunk/twext/protocols/memcache.py	2009-03-11 23:34:25 UTC (rev 3831)
@@ -0,0 +1,666 @@
+# -*- test-case-name: twisted.test.test_memcache -*-
+# Copyright (c) 2007-2009 Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Memcache client protocol. Memcached is a caching server, storing data in the
+form of pairs key/value, and memcache is the protocol to talk with it.
+
+To connect to a server, create a factory for L{MemCacheProtocol}::
+
+    from twisted.internet import reactor, protocol
+    from twisted.protocols.memcache import MemCacheProtocol, DEFAULT_PORT
+    d = protocol.ClientCreator(reactor, MemCacheProtocol
+        ).connectTCP("localhost", DEFAULT_PORT)
+    def doSomething(proto):
+        # Here you call the memcache operations
+        return proto.set("mykey", "a lot of data")
+    d.addCallback(doSomething)
+    reactor.run()
+
+All the operations of the memcache protocol are present, but
+L{MemCacheProtocol.set} and L{MemCacheProtocol.get} are the more important.
+
+See U{http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt} for
+more information about the protocol.
+"""
+
+try:
+    from collections import deque
+except ImportError:
+    class deque(list):
+        def popleft(self):
+            return self.pop(0)
+
+
+from twisted.protocols.basic import LineReceiver
+from twisted.protocols.policies import TimeoutMixin
+from twisted.internet.defer import Deferred, fail, TimeoutError
+from twisted.python import log
+
+
+
+DEFAULT_PORT = 11211
+
+
+
+class NoSuchCommand(Exception):
+    """
+    Exception raised when a non existent command is called.
+    """
+
+
+
+class ClientError(Exception):
+    """
+    Error caused by an invalid client call.
+    """
+
+
+
+class ServerError(Exception):
+    """
+    Problem happening on the server.
+    """
+
+
+
+class Command(object):
+    """
+    Wrap a client action into an object, that holds the values used in the
+    protocol.
+
+    @ivar _deferred: the L{Deferred} object that will be fired when the result
+        arrives.
+    @type _deferred: L{Deferred}
+
+    @ivar command: name of the command sent to the server.
+    @type command: C{str}
+    """
+
+    def __init__(self, command, **kwargs):
+        """
+        Create a command.
+
+        @param command: the name of the command.
+        @type command: C{str}
+
+        @param kwargs: this values will be stored as attributes of the object
+            for future use
+        """
+        self.command = command
+        self._deferred = Deferred()
+        for k, v in kwargs.items():
+            setattr(self, k, v)
+
+
+    def success(self, value):
+        """
+        Shortcut method to fire the underlying deferred.
+        """
+        self._deferred.callback(value)
+
+
+    def fail(self, error):
+        """
+        Make the underlying deferred fails.
+        """
+        self._deferred.errback(error)
+
+
+
+class MemCacheProtocol(LineReceiver, TimeoutMixin):
+    """
+    MemCache protocol: connect to a memcached server to store/retrieve values.
+
+    @ivar persistentTimeOut: the timeout period used to wait for a response.
+    @type persistentTimeOut: C{int}
+
+    @ivar _current: current list of requests waiting for an answer from the
+        server.
+    @type _current: C{deque} of L{Command}
+
+    @ivar _lenExpected: amount of data expected in raw mode, when reading for
+        a value.
+    @type _lenExpected: C{int}
+
+    @ivar _getBuffer: current buffer of data, used to store temporary data
+        when reading in raw mode.
+    @type _getBuffer: C{list}
+
+    @ivar _bufferLength: the total amount of bytes in C{_getBuffer}.
+    @type _bufferLength: C{int}
+    """
+    MAX_KEY_LENGTH = 250
+
+    def __init__(self, timeOut=60):
+        """
+        Create the protocol.
+
+        @param timeOut: the timeout to wait before detecting that the
+            connection is dead and close it. It's expressed in seconds.
+        @type timeOut: C{int}
+        """
+        self._current = deque()
+        self._lenExpected = None
+        self._getBuffer = None
+        self._bufferLength = None
+        self.persistentTimeOut = self.timeOut = timeOut
+
+
+    def timeoutConnection(self):
+        """
+        Close the connection in case of timeout.
+        """
+        for cmd in self._current:
+            cmd.fail(TimeoutError("Connection timeout"))
+        self.transport.loseConnection()
+
+
+    def sendLine(self, line):
+        """
+        Override sendLine to add a timeout to response.
+        """
+        if not self._current:
+           self.setTimeout(self.persistentTimeOut)
+        LineReceiver.sendLine(self, line)
+
+
+    def rawDataReceived(self, data):
+        """
+        Collect data for a get.
+        """
+        self.resetTimeout()
+        self._getBuffer.append(data)
+        self._bufferLength += len(data)
+        if self._bufferLength >= self._lenExpected + 2:
+            data = "".join(self._getBuffer)
+            buf = data[:self._lenExpected]
+            rem = data[self._lenExpected + 2:]
+            val = buf
+            self._lenExpected = None
+            self._getBuffer = None
+            self._bufferLength = None
+            cmd = self._current[0]
+            cmd.value = val
+            self.setLineMode(rem)
+
+
+    def cmd_STORED(self):
+        """
+        Manage a success response to a set operation.
+        """
+        self._current.popleft().success(True)
+
+
+    def cmd_NOT_STORED(self):
+        """
+        Manage a specific 'not stored' response to a set operation: this is not
+        an error, but some condition wasn't met.
+        """
+        self._current.popleft().success(False)
+
+
+    def cmd_END(self):
+        """
+        This the end token to a get or a stat operation.
+        """
+        cmd = self._current.popleft()
+        if cmd.command == "get":
+            cmd.success((cmd.flags, cmd.value))
+        elif cmd.command == "gets":
+            cmd.success((cmd.flags, cmd.cas, cmd.value))
+        elif cmd.command == "stats":
+            cmd.success(cmd.values)
+
+
+    def cmd_NOT_FOUND(self):
+        """
+        Manage error response for incr/decr/delete.
+        """
+        self._current.popleft().success(False)
+
+
+    def cmd_VALUE(self, line):
+        """
+        Prepare the reading a value after a get.
+        """
+        cmd = self._current[0]
+        if cmd.command == "get":
+            key, flags, length = line.split()
+            cas = ""
+        else:
+            key, flags, length, cas = line.split()
+        self._lenExpected = int(length)
+        self._getBuffer = []
+        self._bufferLength = 0
+        if cmd.key != key:
+            raise RuntimeError("Unexpected commands answer.")
+        cmd.flags = int(flags)
+        cmd.length = self._lenExpected
+        cmd.cas = cas
+        self.setRawMode()
+
+
+    def cmd_STAT(self, line):
+        """
+        Reception of one stat line.
+        """
+        cmd = self._current[0]
+        key, val = line.split(" ", 1)
+        cmd.values[key] = val
+
+
+    def cmd_VERSION(self, versionData):
+        """
+        Read version token.
+        """
+        self._current.popleft().success(versionData)
+
+
+    def cmd_ERROR(self):
+        """
+        An non-existent command has been sent.
+        """
+        log.err("Non-existent command sent.")
+        cmd = self._current.popleft()
+        cmd.fail(NoSuchCommand())
+
+
+    def cmd_CLIENT_ERROR(self, errText):
+        """
+        An invalid input as been sent.
+        """
+        log.err("Invalid input: %s" % (errText,))
+        cmd = self._current.popleft()
+        cmd.fail(ClientError(errText))
+
+
+    def cmd_SERVER_ERROR(self, errText):
+        """
+        An error has happened server-side.
+        """
+        log.err("Server error: %s" % (errText,))
+        cmd = self._current.popleft()
+        cmd.fail(ServerError(errText))
+
+
+    def cmd_DELETED(self):
+        """
+        A delete command has completed successfully.
+        """
+        self._current.popleft().success(True)
+
+
+    def cmd_OK(self):
+        """
+        The last command has been completed.
+        """
+        self._current.popleft().success(True)
+
+
+    def cmd_EXISTS(self):
+        """
+        A C{checkAndSet} update has failed.
+        """
+        self._current.popleft().success(False)
+
+
+    def lineReceived(self, line):
+        """
+        Receive line commands from the server.
+        """
+        self.resetTimeout()
+        token = line.split(" ", 1)[0]
+        # First manage standard commands without space
+        cmd = getattr(self, "cmd_%s" % (token,), None)
+        if cmd is not None:
+            args = line.split(" ", 1)[1:]
+            if args:
+                cmd(args[0])
+            else:
+                cmd()
+        else:
+            # Then manage commands with space in it
+            line = line.replace(" ", "_")
+            cmd = getattr(self, "cmd_%s" % (line,), None)
+            if cmd is not None:
+                cmd()
+            else:
+                # Increment/Decrement response
+                cmd = self._current.popleft()
+                val = int(line)
+                cmd.success(val)
+        if not self._current:
+            # No pending request, remove timeout
+            self.setTimeout(None)
+
+
+    def increment(self, key, val=1):
+        """
+        Increment the value of C{key} by given value (default to 1).
+        C{key} must be consistent with an int. Return the new value.
+
+        @param key: the key to modify.
+        @type key: C{str}
+
+        @param val: the value to increment.
+        @type val: C{int}
+
+        @return: a deferred with will be called back with the new value
+            associated with the key (after the increment).
+        @rtype: L{Deferred}
+        """
+        return self._incrdecr("incr", key, val)
+
+
+    def decrement(self, key, val=1):
+        """
+        Decrement the value of C{key} by given value (default to 1).
+        C{key} must be consistent with an int. Return the new value, coerced to
+        0 if negative.
+
+        @param key: the key to modify.
+        @type key: C{str}
+
+        @param val: the value to decrement.
+        @type val: C{int}
+
+        @return: a deferred with will be called back with the new value
+            associated with the key (after the decrement).
+        @rtype: L{Deferred}
+        """
+        return self._incrdecr("decr", key, val)
+
+
+    def _incrdecr(self, cmd, key, val):
+        """
+        Internal wrapper for incr/decr.
+        """
+        if not isinstance(key, str):
+            return fail(ClientError(
+                "Invalid type for key: %s, expecting a string" % (type(key),)))
+        if len(key) > self.MAX_KEY_LENGTH:
+            return fail(ClientError("Key too long"))
+        fullcmd = "%s %s %d" % (cmd, key, int(val))
+        self.sendLine(fullcmd)
+        cmdObj = Command(cmd, key=key)
+        self._current.append(cmdObj)
+        return cmdObj._deferred
+
+
+    def replace(self, key, val, flags=0, expireTime=0):
+        """
+        Replace the given C{key}. It must already exist in the server.
+
+        @param key: the key to replace.
+        @type key: C{str}
+
+        @param val: the new value associated with the key.
+        @type val: C{str}
+
+        @param flags: the flags to store with the key.
+        @type flags: C{int}
+
+        @param expireTime: if different from 0, the relative time in seconds
+            when the key will be deleted from the store.
+        @type expireTime: C{int}
+
+        @return: a deferred that will fire with C{True} if the operation has
+            succeeded, and C{False} with the key didn't previously exist.
+        @rtype: L{Deferred}
+        """
+        return self._set("replace", key, val, flags, expireTime, "")
+
+
+    def add(self, key, val, flags=0, expireTime=0):
+        """
+        Add the given C{key}. It must not exist in the server.
+
+        @param key: the key to add.
+        @type key: C{str}
+
+        @param val: the value associated with the key.
+        @type val: C{str}
+
+        @param flags: the flags to store with the key.
+        @type flags: C{int}
+
+        @param expireTime: if different from 0, the relative time in seconds
+            when the key will be deleted from the store.
+        @type expireTime: C{int}
+
+        @return: a deferred that will fire with C{True} if the operation has
+            succeeded, and C{False} with the key already exists.
+        @rtype: L{Deferred}
+        """
+        return self._set("add", key, val, flags, expireTime, "")
+
+
+    def set(self, key, val, flags=0, expireTime=0):
+        """
+        Set the given C{key}.
+
+        @param key: the key to set.
+        @type key: C{str}
+
+        @param val: the value associated with the key.
+        @type val: C{str}
+
+        @param flags: the flags to store with the key.
+        @type flags: C{int}
+
+        @param expireTime: if different from 0, the relative time in seconds
+            when the key will be deleted from the store.
+        @type expireTime: C{int}
+
+        @return: a deferred that will fire with C{True} if the operation has
+            succeeded.
+        @rtype: L{Deferred}
+        """
+        return self._set("set", key, val, flags, expireTime, "")
+
+
+    def checkAndSet(self, key, val, cas, flags=0, expireTime=0):
+        """
+        Change the content of C{key} only if the C{cas} value matches the
+        current one associated with the key. Use this to store a value which
+        hasn't been modified since last time you fetched it.
+
+        @param key: The key to set.
+        @type key: C{str}
+
+        @param val: The value associated with the key.
+        @type val: C{str}
+
+        @param cas: Unique 64-bit value returned by previous call of C{get}.
+        @type cas: C{str}
+
+        @param flags: The flags to store with the key.
+        @type flags: C{int}
+
+        @param expireTime: If different from 0, the relative time in seconds
+            when the key will be deleted from the store.
+        @type expireTime: C{int}
+
+        @return: A deferred that will fire with C{True} if the operation has
+            succeeded, C{False} otherwise.
+        @rtype: L{Deferred}
+        """
+        return self._set("cas", key, val, flags, expireTime, cas)
+
+
+    def _set(self, cmd, key, val, flags, expireTime, cas):
+        """
+        Internal wrapper for setting values.
+        """
+        if not isinstance(key, str):
+            return fail(ClientError(
+                "Invalid type for key: %s, expecting a string" % (type(key),)))
+        if len(key) > self.MAX_KEY_LENGTH:
+            return fail(ClientError("Key too long"))
+        if not isinstance(val, str):
+            return fail(ClientError(
+                "Invalid type for value: %s, expecting a string" %
+                (type(val),)))
+        if cas:
+            cas = " " + cas
+        length = len(val)
+        fullcmd = "%s %s %d %d %d%s" % (
+            cmd, key, flags, expireTime, length, cas)
+        self.sendLine(fullcmd)
+        self.sendLine(val)
+        cmdObj = Command(cmd, key=key, flags=flags, length=length)
+        self._current.append(cmdObj)
+        return cmdObj._deferred
+
+
+    def append(self, key, val):
+        """
+        Append given data to the value of an existing key.
+
+        @param key: The key to modify.
+        @type key: C{str}
+
+        @param val: The value to append to the current value associated with
+            the key.
+        @type val: C{str}
+
+        @return: A deferred that will fire with C{True} if the operation has
+            succeeded, C{False} otherwise.
+        @rtype: L{Deferred}
+        """
+        # Even if flags and expTime values are ignored, we have to pass them
+        return self._set("append", key, val, 0, 0, "")
+
+
+    def prepend(self, key, val):
+        """
+        Prepend given data to the value of an existing key.
+
+        @param key: The key to modify.
+        @type key: C{str}
+
+        @param val: The value to prepend to the current value associated with
+            the key.
+        @type val: C{str}
+
+        @return: A deferred that will fire with C{True} if the operation has
+            succeeded, C{False} otherwise.
+        @rtype: L{Deferred}
+        """
+        # Even if flags and expTime values are ignored, we have to pass them
+        return self._set("prepend", key, val, 0, 0, "")
+
+
+    def get(self, key, withIdentifier=False):
+        """
+        Get the given C{key}. It doesn't support multiple keys. If
+        C{withIdentifier} is set to C{True}, the command issued is a C{gets},
+        that will return the current identifier associated with the value. This
+        identifier has to be used when issuing C{checkAndSet} update later,
+        using the corresponding method.
+
+        @param key: The key to retrieve.
+        @type key: C{str}
+
+        @param withIdentifier: If set to C{True}, retrieve the current
+            identifier along with the value and the flags.
+        @type withIdentifier: C{bool}
+
+        @return: A deferred that will fire with the tuple (flags, value) if
+            C{withIdentifier} is C{False}, or (flags, cas identifier, value)
+            if C{True}.
+        @rtype: L{Deferred}
+        """
+        if not isinstance(key, str):
+            return fail(ClientError(
+                "Invalid type for key: %s, expecting a string" % (type(key),)))
+        if len(key) > self.MAX_KEY_LENGTH:
+            return fail(ClientError("Key too long"))
+        if withIdentifier:
+            cmd = "gets"
+        else:
+            cmd = "get"
+        fullcmd = "%s %s" % (cmd, key)
+        self.sendLine(fullcmd)
+        cmdObj = Command(cmd, key=key, value=None, flags=0, cas="")
+        self._current.append(cmdObj)
+        return cmdObj._deferred
+
+
+    def stats(self, arg=None):
+        """
+        Get some stats from the server. It will be available as a dict.
+
+        @param arg: An optional additional string which will be sent along
+            with the I{stats} command.  The interpretation of this value by
+            the server is left undefined by the memcache protocol
+            specification.
+        @type arg: L{NoneType} or L{str}
+
+        @return: a deferred that will fire with a C{dict} of the available
+            statistics.
+        @rtype: L{Deferred}
+        """
+        cmd = "stats"
+        if arg:
+            cmd = "stats " + arg
+        self.sendLine(cmd)
+        cmdObj = Command("stats", values={})
+        self._current.append(cmdObj)
+        return cmdObj._deferred
+
+
+    def version(self):
+        """
+        Get the version of the server.
+
+        @return: a deferred that will fire with the string value of the
+            version.
+        @rtype: L{Deferred}
+        """
+        self.sendLine("version")
+        cmdObj = Command("version")
+        self._current.append(cmdObj)
+        return cmdObj._deferred
+
+
+    def delete(self, key):
+        """
+        Delete an existing C{key}.
+
+        @param key: the key to delete.
+        @type key: C{str}
+
+        @return: a deferred that will be called back with C{True} if the key
+            was successfully deleted, or C{False} if not.
+        @rtype: L{Deferred}
+        """
+        if not isinstance(key, str):
+            return fail(ClientError(
+                "Invalid type for key: %s, expecting a string" % (type(key),)))
+        self.sendLine("delete %s" % key)
+        cmdObj = Command("delete", key=key)
+        self._current.append(cmdObj)
+        return cmdObj._deferred
+
+
+    def flushAll(self):
+        """
+        Flush all cached values.
+
+        @return: a deferred that will be called back with C{True} when the
+            operation has succeeded.
+        @rtype: L{Deferred}
+        """
+        self.sendLine("flush_all")
+        cmdObj = Command("flush_all")
+        self._current.append(cmdObj)
+        return cmdObj._deferred
+
+
+
+__all__ = ["MemCacheProtocol", "DEFAULT_PORT", "NoSuchCommand", "ClientError",
+           "ServerError"]
+

Added: CalendarServer/trunk/twext/protocols/test/__init__.py
===================================================================
--- CalendarServer/trunk/twext/protocols/test/__init__.py	                        (rev 0)
+++ CalendarServer/trunk/twext/protocols/test/__init__.py	2009-03-11 23:34:25 UTC (rev 3831)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2009 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.
+##
+
+"""
+Extentions to twisted.protocols
+"""

Copied: CalendarServer/trunk/twext/protocols/test/test_memcache.py (from rev 3829, CalendarServer/trunk/twistedcaldav/test/test_memcache.py)
===================================================================
--- CalendarServer/trunk/twext/protocols/test/test_memcache.py	                        (rev 0)
+++ CalendarServer/trunk/twext/protocols/test/test_memcache.py	2009-03-11 23:34:25 UTC (rev 3831)
@@ -0,0 +1,523 @@
+# Copyright (c) 2007-2009 Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Test the memcache client protocol.
+"""
+
+from twext.protocols.memcache import MemCacheProtocol, NoSuchCommand
+from twext.protocols.memcache import ClientError, ServerError
+
+from twisted.trial.unittest import TestCase
+from twisted.test.proto_helpers import StringTransportWithDisconnection
+from twisted.internet.task import Clock
+from twisted.internet.defer import Deferred, gatherResults, TimeoutError
+
+
+
+class MemCacheTestCase(TestCase):
+    """
+    Test client protocol class L{MemCacheProtocol}.
+    """
+
+    def setUp(self):
+        """
+        Create a memcache client, connect it to a string protocol, and make it
+        use a deterministic clock.
+        """
+        self.proto = MemCacheProtocol()
+        self.clock = Clock()
+        self.proto.callLater = self.clock.callLater
+        self.transport = StringTransportWithDisconnection()
+        self.transport.protocol = self.proto
+        self.proto.makeConnection(self.transport)
+
+
+    def _test(self, d, send, recv, result):
+        """
+        Shortcut method for classic tests.
+
+        @param d: the resulting deferred from the memcache command.
+        @type d: C{Deferred}
+
+        @param send: the expected data to be sent.
+        @type send: C{str}
+
+        @param recv: the data to simulate as reception.
+        @type recv: C{str}
+
+        @param result: the expected result.
+        @type result: C{any}
+        """
+        def cb(res):
+            self.assertEquals(res, result)
+        self.assertEquals(self.transport.value(), send)
+        d.addCallback(cb)
+        self.proto.dataReceived(recv)
+        return d
+
+
+    def test_get(self):
+        """
+        L{MemCacheProtocol.get} should return a L{Deferred} which is
+        called back with the value and the flag associated with the given key
+        if the server returns a successful result.
+        """
+        return self._test(self.proto.get("foo"), "get foo\r\n",
+            "VALUE foo 0 3\r\nbar\r\nEND\r\n", (0, "bar"))
+
+
+    def test_emptyGet(self):
+        """
+        Test getting a non-available key: it should succeed but return C{None}
+        as value and C{0} as flag.
+        """
+        return self._test(self.proto.get("foo"), "get foo\r\n",
+            "END\r\n", (0, None))
+
+
+    def test_set(self):
+        """
+        L{MemCacheProtocol.set} should return a L{Deferred} which is
+        called back with C{True} when the operation succeeds.
+        """
+        return self._test(self.proto.set("foo", "bar"),
+            "set foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
+
+
+    def test_add(self):
+        """
+        L{MemCacheProtocol.add} should return a L{Deferred} which is
+        called back with C{True} when the operation succeeds.
+        """
+        return self._test(self.proto.add("foo", "bar"),
+            "add foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
+
+
+    def test_replace(self):
+        """
+        L{MemCacheProtocol.replace} should return a L{Deferred} which
+        is called back with C{True} when the operation succeeds.
+        """
+        return self._test(self.proto.replace("foo", "bar"),
+            "replace foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
+
+
+    def test_errorAdd(self):
+        """
+        Test an erroneous add: if a L{MemCacheProtocol.add} is called but the
+        key already exists on the server, it returns a B{NOT STORED} answer,
+        which should callback the resulting L{Deferred} with C{False}.
+        """
+        return self._test(self.proto.add("foo", "bar"),
+            "add foo 0 0 3\r\nbar\r\n", "NOT STORED\r\n", False)
+
+
+    def test_errorReplace(self):
+        """
+        Test an erroneous replace: if a L{MemCacheProtocol.replace} is called
+        but the key doesn't exist on the server, it returns a B{NOT STORED}
+        answer, which should callback the resulting L{Deferred} with C{False}.
+        """
+        return self._test(self.proto.replace("foo", "bar"),
+            "replace foo 0 0 3\r\nbar\r\n", "NOT STORED\r\n", False)
+
+
+    def test_delete(self):
+        """
+        L{MemCacheProtocol.delete} should return a L{Deferred} which is
+        called back with C{True} when the server notifies a success.
+        """
+        return self._test(self.proto.delete("bar"), "delete bar\r\n",
+            "DELETED\r\n", True)
+
+
+    def test_errorDelete(self):
+        """
+        Test a error during a delete: if key doesn't exist on the server, it
+        returns a B{NOT FOUND} answer which should callback the resulting
+        L{Deferred} with C{False}.
+        """
+        return self._test(self.proto.delete("bar"), "delete bar\r\n",
+            "NOT FOUND\r\n", False)
+
+
+    def test_increment(self):
+        """
+        Test incrementing a variable: L{MemCacheProtocol.increment} should
+        return a L{Deferred} which is called back with the incremented value of
+        the given key.
+        """
+        return self._test(self.proto.increment("foo"), "incr foo 1\r\n",
+            "4\r\n", 4)
+
+
+    def test_decrement(self):
+        """
+        Test decrementing a variable: L{MemCacheProtocol.decrement} should
+        return a L{Deferred} which is called back with the decremented value of
+        the given key.
+        """
+        return self._test(
+            self.proto.decrement("foo"), "decr foo 1\r\n", "5\r\n", 5)
+
+
+    def test_incrementVal(self):
+        """
+        L{MemCacheProtocol.increment} takes an optional argument C{value} which
+        should replace the default value of 1 when specified.
+        """
+        return self._test(self.proto.increment("foo", 8), "incr foo 8\r\n",
+            "4\r\n", 4)
+
+
+    def test_decrementVal(self):
+        """
+        L{MemCacheProtocol.decrement} takes an optional argument C{value} which
+        should replace the default value of 1 when specified.
+        """
+        return self._test(self.proto.decrement("foo", 3), "decr foo 3\r\n",
+            "5\r\n", 5)
+
+
+    def test_stats(self):
+        """
+        Test retrieving server statistics via the L{MemCacheProtocol.stats}
+        command: it should parse the data sent by the server and call back the
+        resulting L{Deferred} with a dictionary of the received statistics.
+        """
+        return self._test(self.proto.stats(), "stats\r\n",
+            "STAT foo bar\r\nSTAT egg spam\r\nEND\r\n",
+            {"foo": "bar", "egg": "spam"})
+
+
+    def test_statsWithArgument(self):
+        """
+
+        L{MemCacheProtocol.stats} takes an optional C{str} argument which,
+        if specified, is sent along with the I{STAT} command.  The I{STAT}
+        responses from the server are parsed as key/value pairs and returned
+        as a C{dict} (as in the case where the argument is not specified).
+        """
+        return self._test(self.proto.stats("blah"), "stats blah\r\n",
+            "STAT foo bar\r\nSTAT egg spam\r\nEND\r\n",
+            {"foo": "bar", "egg": "spam"})
+
+
+    def test_version(self):
+        """
+        Test version retrieval via the L{MemCacheProtocol.version} command: it
+        should return a L{Deferred} which is called back with the version sent
+        by the server.
+        """
+        return self._test(self.proto.version(), "version\r\n",
+            "VERSION 1.1\r\n", "1.1")
+
+
+    def test_flushAll(self):
+        """
+        L{MemCacheProtocol.flushAll} should return a L{Deferred} which is
+        called back with C{True} if the server acknowledges success.
+        """
+        return self._test(self.proto.flushAll(), "flush_all\r\n",
+            "OK\r\n", True)
+
+
+    def test_invalidGetResponse(self):
+        """
+        If the value returned doesn't match the expected key of the current, we
+        should get an error in L{MemCacheProtocol.dataReceived}.
+        """
+        self.proto.get("foo")
+        s = "spamegg"
+        self.assertRaises(RuntimeError,
+            self.proto.dataReceived,
+            "VALUE bar 0 %s\r\n%s\r\nEND\r\n" % (len(s), s))
+
+
+    def test_timeOut(self):
+        """
+        Test the timeout on outgoing requests: when timeout is detected, all
+        current commands should fail with a L{TimeoutError}, and the
+        connection should be closed.
+        """
+        d1 = self.proto.get("foo")
+        d2 = self.proto.get("bar")
+        d3 = Deferred()
+        self.proto.connectionLost = d3.callback
+
+        self.clock.advance(self.proto.persistentTimeOut)
+        self.assertFailure(d1, TimeoutError)
+        self.assertFailure(d2, TimeoutError)
+        def checkMessage(error):
+            self.assertEquals(str(error), "Connection timeout")
+        d1.addCallback(checkMessage)
+        return gatherResults([d1, d2, d3])
+
+
+    def test_timeoutRemoved(self):
+        """
+        When a request gets a response, no pending timeout call should remain
+        around.
+        """
+        d = self.proto.get("foo")
+
+        self.clock.advance(self.proto.persistentTimeOut - 1)
+        self.proto.dataReceived("VALUE foo 0 3\r\nbar\r\nEND\r\n")
+
+        def check(result):
+            self.assertEquals(result, (0, "bar"))
+            self.assertEquals(len(self.clock.calls), 0)
+        d.addCallback(check)
+        return d
+
+
+    def test_timeOutRaw(self):
+        """
+        Test the timeout when raw mode was started: the timeout should not be
+        reset until all the data has been received, so we can have a
+        L{TimeoutError} when waiting for raw data.
+        """
+        d1 = self.proto.get("foo")
+        d2 = Deferred()
+        self.proto.connectionLost = d2.callback
+
+        self.proto.dataReceived("VALUE foo 0 10\r\n12345")
+        self.clock.advance(self.proto.persistentTimeOut)
+        self.assertFailure(d1, TimeoutError)
+        return gatherResults([d1, d2])
+
+
+    def test_timeOutStat(self):
+        """
+        Test the timeout when stat command has started: the timeout should not
+        be reset until the final B{END} is received.
+        """
+        d1 = self.proto.stats()
+        d2 = Deferred()
+        self.proto.connectionLost = d2.callback
+
+        self.proto.dataReceived("STAT foo bar\r\n")
+        self.clock.advance(self.proto.persistentTimeOut)
+        self.assertFailure(d1, TimeoutError)
+        return gatherResults([d1, d2])
+
+
+    def test_timeoutPipelining(self):
+        """
+        When two requests are sent, a timeout call should remain around for the
+        second request, and its timeout time should be correct.
+        """
+        d1 = self.proto.get("foo")
+        d2 = self.proto.get("bar")
+        d3 = Deferred()
+        self.proto.connectionLost = d3.callback
+
+        self.clock.advance(self.proto.persistentTimeOut - 1)
+        self.proto.dataReceived("VALUE foo 0 3\r\nbar\r\nEND\r\n")
+
+        def check(result):
+            self.assertEquals(result, (0, "bar"))
+            self.assertEquals(len(self.clock.calls), 1)
+            for i in range(self.proto.persistentTimeOut):
+                self.clock.advance(1)
+            return self.assertFailure(d2, TimeoutError).addCallback(checkTime)
+        def checkTime(ignored):
+            # Check that the timeout happened C{self.proto.persistentTimeOut}
+            # after the last response
+            self.assertEquals(self.clock.seconds(),
+                    2 * self.proto.persistentTimeOut - 1)
+        d1.addCallback(check)
+        return d1
+
+
+    def test_timeoutNotReset(self):
+        """
+        Check that timeout is not resetted for every command, but keep the
+        timeout from the first command without response.
+        """
+        d1 = self.proto.get("foo")
+        d3 = Deferred()
+        self.proto.connectionLost = d3.callback
+
+        self.clock.advance(self.proto.persistentTimeOut - 1)
+        d2 = self.proto.get("bar")
+        self.clock.advance(1)
+        self.assertFailure(d1, TimeoutError)
+        self.assertFailure(d2, TimeoutError)
+        return gatherResults([d1, d2, d3])
+
+
+    def test_tooLongKey(self):
+        """
+        Test that an error is raised when trying to use a too long key: the
+        called command should return a L{Deferred} which fail with a
+        L{ClientError}.
+        """
+        d1 = self.assertFailure(self.proto.set("a" * 500, "bar"), ClientError)
+        d2 = self.assertFailure(self.proto.increment("a" * 500), ClientError)
+        d3 = self.assertFailure(self.proto.get("a" * 500), ClientError)
+        d4 = self.assertFailure(self.proto.append("a" * 500, "bar"), ClientError)
+        d5 = self.assertFailure(self.proto.prepend("a" * 500, "bar"), ClientError)
+        return gatherResults([d1, d2, d3, d4, d5])
+
+
+    def test_invalidCommand(self):
+        """
+        When an unknown command is sent directly (not through public API), the
+        server answers with an B{ERROR} token, and the command should fail with
+        L{NoSuchCommand}.
+        """
+        d = self.proto._set("egg", "foo", "bar", 0, 0, "")
+        self.assertEquals(self.transport.value(), "egg foo 0 0 3\r\nbar\r\n")
+        self.assertFailure(d, NoSuchCommand)
+        self.proto.dataReceived("ERROR\r\n")
+        return d
+
+
+    def test_clientError(self):
+        """
+        Test the L{ClientError} error: when the server send a B{CLIENT_ERROR}
+        token, the originating command should fail with L{ClientError}, and the
+        error should contain the text sent by the server.
+        """
+        a = "eggspamm"
+        d = self.proto.set("foo", a)
+        self.assertEquals(self.transport.value(),
+                          "set foo 0 0 8\r\neggspamm\r\n")
+        self.assertFailure(d, ClientError)
+        def check(err):
+            self.assertEquals(str(err), "We don't like egg and spam")
+        d.addCallback(check)
+        self.proto.dataReceived("CLIENT_ERROR We don't like egg and spam\r\n")
+        return d
+
+
+    def test_serverError(self):
+        """
+        Test the L{ServerError} error: when the server send a B{SERVER_ERROR}
+        token, the originating command should fail with L{ServerError}, and the
+        error should contain the text sent by the server.
+        """
+        a = "eggspamm"
+        d = self.proto.set("foo", a)
+        self.assertEquals(self.transport.value(),
+                          "set foo 0 0 8\r\neggspamm\r\n")
+        self.assertFailure(d, ServerError)
+        def check(err):
+            self.assertEquals(str(err), "zomg")
+        d.addCallback(check)
+        self.proto.dataReceived("SERVER_ERROR zomg\r\n")
+        return d
+
+
+    def test_unicodeKey(self):
+        """
+        Using a non-string key as argument to commands should raise an error.
+        """
+        d1 = self.assertFailure(self.proto.set(u"foo", "bar"), ClientError)
+        d2 = self.assertFailure(self.proto.increment(u"egg"), ClientError)
+        d3 = self.assertFailure(self.proto.get(1), ClientError)
+        d4 = self.assertFailure(self.proto.delete(u"bar"), ClientError)
+        d5 = self.assertFailure(self.proto.append(u"foo", "bar"), ClientError)
+        d6 = self.assertFailure(self.proto.prepend(u"foo", "bar"), ClientError)
+        return gatherResults([d1, d2, d3, d4, d5, d6])
+
+
+    def test_unicodeValue(self):
+        """
+        Using a non-string value should raise an error.
+        """
+        return self.assertFailure(self.proto.set("foo", u"bar"), ClientError)
+
+
+    def test_pipelining(self):
+        """
+        Test that multiple requests can be sent subsequently to the server, and
+        that the protocol order the responses correctly and dispatch to the
+        corresponding client command.
+        """
+        d1 = self.proto.get("foo")
+        d1.addCallback(self.assertEquals, (0, "bar"))
+        d2 = self.proto.set("bar", "spamspamspam")
+        d2.addCallback(self.assertEquals, True)
+        d3 = self.proto.get("egg")
+        d3.addCallback(self.assertEquals, (0, "spam"))
+        self.assertEquals(self.transport.value(),
+            "get foo\r\nset bar 0 0 12\r\nspamspamspam\r\nget egg\r\n")
+        self.proto.dataReceived("VALUE foo 0 3\r\nbar\r\nEND\r\n"
+                                "STORED\r\n"
+                                "VALUE egg 0 4\r\nspam\r\nEND\r\n")
+        return gatherResults([d1, d2, d3])
+
+
+    def test_getInChunks(self):
+        """
+        If the value retrieved by a C{get} arrive in chunks, the protocol
+        should be able to reconstruct it and to produce the good value.
+        """
+        d = self.proto.get("foo")
+        d.addCallback(self.assertEquals, (0, "0123456789"))
+        self.assertEquals(self.transport.value(), "get foo\r\n")
+        self.proto.dataReceived("VALUE foo 0 10\r\n0123456")
+        self.proto.dataReceived("789")
+        self.proto.dataReceived("\r\nEND")
+        self.proto.dataReceived("\r\n")
+        return d
+
+
+    def test_append(self):
+        """
+        L{MemCacheProtocol.append} behaves like a L{MemCacheProtocol.set}
+        method: it should return a L{Deferred} which is called back with
+        C{True} when the operation succeeds.
+        """
+        return self._test(self.proto.append("foo", "bar"),
+            "append foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
+
+
+    def test_prepend(self):
+        """
+        L{MemCacheProtocol.prepend} behaves like a L{MemCacheProtocol.set}
+        method: it should return a L{Deferred} which is called back with
+        C{True} when the operation succeeds.
+        """
+        return self._test(self.proto.prepend("foo", "bar"),
+            "prepend foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
+
+
+    def test_gets(self):
+        """
+        L{MemCacheProtocol.get} should handle an additional cas result when
+        C{withIdentifier} is C{True} and forward it in the resulting
+        L{Deferred}.
+        """
+        return self._test(self.proto.get("foo", True), "gets foo\r\n",
+            "VALUE foo 0 3 1234\r\nbar\r\nEND\r\n", (0, "1234", "bar"))
+
+
+    def test_emptyGets(self):
+        """
+        Test getting a non-available key with gets: it should succeed but
+        return C{None} as value, C{0} as flag and an empty cas value.
+        """
+        return self._test(self.proto.get("foo", True), "gets foo\r\n",
+            "END\r\n", (0, "", None))
+
+
+    def test_checkAndSet(self):
+        """
+        L{MemCacheProtocol.checkAndSet} passes an additional cas identifier that the
+        server should handle to check if the data has to be updated.
+        """
+        return self._test(self.proto.checkAndSet("foo", "bar", cas="1234"),
+            "cas foo 0 0 3 1234\r\nbar\r\n", "STORED\r\n", True)
+
+
+    def test_casUnknowKey(self):
+        """
+        When L{MemCacheProtocol.checkAndSet} response is C{EXISTS}, the resulting
+        L{Deferred} should fire with C{False}.
+        """
+        return self._test(self.proto.checkAndSet("foo", "bar", cas="1234"),
+            "cas foo 0 0 3 1234\r\nbar\r\n", "EXISTS\r\n", False)

Deleted: CalendarServer/trunk/twistedcaldav/memcache.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/memcache.py	2009-03-11 23:33:58 UTC (rev 3830)
+++ CalendarServer/trunk/twistedcaldav/memcache.py	2009-03-11 23:34:25 UTC (rev 3831)
@@ -1,666 +0,0 @@
-# -*- test-case-name: twisted.test.test_memcache -*-
-# Copyright (c) 2007-2009 Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-"""
-Memcache client protocol. Memcached is a caching server, storing data in the
-form of pairs key/value, and memcache is the protocol to talk with it.
-
-To connect to a server, create a factory for L{MemCacheProtocol}::
-
-    from twisted.internet import reactor, protocol
-    from twisted.protocols.memcache import MemCacheProtocol, DEFAULT_PORT
-    d = protocol.ClientCreator(reactor, MemCacheProtocol
-        ).connectTCP("localhost", DEFAULT_PORT)
-    def doSomething(proto):
-        # Here you call the memcache operations
-        return proto.set("mykey", "a lot of data")
-    d.addCallback(doSomething)
-    reactor.run()
-
-All the operations of the memcache protocol are present, but
-L{MemCacheProtocol.set} and L{MemCacheProtocol.get} are the more important.
-
-See U{http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt} for
-more information about the protocol.
-"""
-
-try:
-    from collections import deque
-except ImportError:
-    class deque(list):
-        def popleft(self):
-            return self.pop(0)
-
-
-from twisted.protocols.basic import LineReceiver
-from twisted.protocols.policies import TimeoutMixin
-from twisted.internet.defer import Deferred, fail, TimeoutError
-from twisted.python import log
-
-
-
-DEFAULT_PORT = 11211
-
-
-
-class NoSuchCommand(Exception):
-    """
-    Exception raised when a non existent command is called.
-    """
-
-
-
-class ClientError(Exception):
-    """
-    Error caused by an invalid client call.
-    """
-
-
-
-class ServerError(Exception):
-    """
-    Problem happening on the server.
-    """
-
-
-
-class Command(object):
-    """
-    Wrap a client action into an object, that holds the values used in the
-    protocol.
-
-    @ivar _deferred: the L{Deferred} object that will be fired when the result
-        arrives.
-    @type _deferred: L{Deferred}
-
-    @ivar command: name of the command sent to the server.
-    @type command: C{str}
-    """
-
-    def __init__(self, command, **kwargs):
-        """
-        Create a command.
-
-        @param command: the name of the command.
-        @type command: C{str}
-
-        @param kwargs: this values will be stored as attributes of the object
-            for future use
-        """
-        self.command = command
-        self._deferred = Deferred()
-        for k, v in kwargs.items():
-            setattr(self, k, v)
-
-
-    def success(self, value):
-        """
-        Shortcut method to fire the underlying deferred.
-        """
-        self._deferred.callback(value)
-
-
-    def fail(self, error):
-        """
-        Make the underlying deferred fails.
-        """
-        self._deferred.errback(error)
-
-
-
-class MemCacheProtocol(LineReceiver, TimeoutMixin):
-    """
-    MemCache protocol: connect to a memcached server to store/retrieve values.
-
-    @ivar persistentTimeOut: the timeout period used to wait for a response.
-    @type persistentTimeOut: C{int}
-
-    @ivar _current: current list of requests waiting for an answer from the
-        server.
-    @type _current: C{deque} of L{Command}
-
-    @ivar _lenExpected: amount of data expected in raw mode, when reading for
-        a value.
-    @type _lenExpected: C{int}
-
-    @ivar _getBuffer: current buffer of data, used to store temporary data
-        when reading in raw mode.
-    @type _getBuffer: C{list}
-
-    @ivar _bufferLength: the total amount of bytes in C{_getBuffer}.
-    @type _bufferLength: C{int}
-    """
-    MAX_KEY_LENGTH = 250
-
-    def __init__(self, timeOut=60):
-        """
-        Create the protocol.
-
-        @param timeOut: the timeout to wait before detecting that the
-            connection is dead and close it. It's expressed in seconds.
-        @type timeOut: C{int}
-        """
-        self._current = deque()
-        self._lenExpected = None
-        self._getBuffer = None
-        self._bufferLength = None
-        self.persistentTimeOut = self.timeOut = timeOut
-
-
-    def timeoutConnection(self):
-        """
-        Close the connection in case of timeout.
-        """
-        for cmd in self._current:
-            cmd.fail(TimeoutError("Connection timeout"))
-        self.transport.loseConnection()
-
-
-    def sendLine(self, line):
-        """
-        Override sendLine to add a timeout to response.
-        """
-        if not self._current:
-           self.setTimeout(self.persistentTimeOut)
-        LineReceiver.sendLine(self, line)
-
-
-    def rawDataReceived(self, data):
-        """
-        Collect data for a get.
-        """
-        self.resetTimeout()
-        self._getBuffer.append(data)
-        self._bufferLength += len(data)
-        if self._bufferLength >= self._lenExpected + 2:
-            data = "".join(self._getBuffer)
-            buf = data[:self._lenExpected]
-            rem = data[self._lenExpected + 2:]
-            val = buf
-            self._lenExpected = None
-            self._getBuffer = None
-            self._bufferLength = None
-            cmd = self._current[0]
-            cmd.value = val
-            self.setLineMode(rem)
-
-
-    def cmd_STORED(self):
-        """
-        Manage a success response to a set operation.
-        """
-        self._current.popleft().success(True)
-
-
-    def cmd_NOT_STORED(self):
-        """
-        Manage a specific 'not stored' response to a set operation: this is not
-        an error, but some condition wasn't met.
-        """
-        self._current.popleft().success(False)
-
-
-    def cmd_END(self):
-        """
-        This the end token to a get or a stat operation.
-        """
-        cmd = self._current.popleft()
-        if cmd.command == "get":
-            cmd.success((cmd.flags, cmd.value))
-        elif cmd.command == "gets":
-            cmd.success((cmd.flags, cmd.cas, cmd.value))
-        elif cmd.command == "stats":
-            cmd.success(cmd.values)
-
-
-    def cmd_NOT_FOUND(self):
-        """
-        Manage error response for incr/decr/delete.
-        """
-        self._current.popleft().success(False)
-
-
-    def cmd_VALUE(self, line):
-        """
-        Prepare the reading a value after a get.
-        """
-        cmd = self._current[0]
-        if cmd.command == "get":
-            key, flags, length = line.split()
-            cas = ""
-        else:
-            key, flags, length, cas = line.split()
-        self._lenExpected = int(length)
-        self._getBuffer = []
-        self._bufferLength = 0
-        if cmd.key != key:
-            raise RuntimeError("Unexpected commands answer.")
-        cmd.flags = int(flags)
-        cmd.length = self._lenExpected
-        cmd.cas = cas
-        self.setRawMode()
-
-
-    def cmd_STAT(self, line):
-        """
-        Reception of one stat line.
-        """
-        cmd = self._current[0]
-        key, val = line.split(" ", 1)
-        cmd.values[key] = val
-
-
-    def cmd_VERSION(self, versionData):
-        """
-        Read version token.
-        """
-        self._current.popleft().success(versionData)
-
-
-    def cmd_ERROR(self):
-        """
-        An non-existent command has been sent.
-        """
-        log.err("Non-existent command sent.")
-        cmd = self._current.popleft()
-        cmd.fail(NoSuchCommand())
-
-
-    def cmd_CLIENT_ERROR(self, errText):
-        """
-        An invalid input as been sent.
-        """
-        log.err("Invalid input: %s" % (errText,))
-        cmd = self._current.popleft()
-        cmd.fail(ClientError(errText))
-
-
-    def cmd_SERVER_ERROR(self, errText):
-        """
-        An error has happened server-side.
-        """
-        log.err("Server error: %s" % (errText,))
-        cmd = self._current.popleft()
-        cmd.fail(ServerError(errText))
-
-
-    def cmd_DELETED(self):
-        """
-        A delete command has completed successfully.
-        """
-        self._current.popleft().success(True)
-
-
-    def cmd_OK(self):
-        """
-        The last command has been completed.
-        """
-        self._current.popleft().success(True)
-
-
-    def cmd_EXISTS(self):
-        """
-        A C{checkAndSet} update has failed.
-        """
-        self._current.popleft().success(False)
-
-
-    def lineReceived(self, line):
-        """
-        Receive line commands from the server.
-        """
-        self.resetTimeout()
-        token = line.split(" ", 1)[0]
-        # First manage standard commands without space
-        cmd = getattr(self, "cmd_%s" % (token,), None)
-        if cmd is not None:
-            args = line.split(" ", 1)[1:]
-            if args:
-                cmd(args[0])
-            else:
-                cmd()
-        else:
-            # Then manage commands with space in it
-            line = line.replace(" ", "_")
-            cmd = getattr(self, "cmd_%s" % (line,), None)
-            if cmd is not None:
-                cmd()
-            else:
-                # Increment/Decrement response
-                cmd = self._current.popleft()
-                val = int(line)
-                cmd.success(val)
-        if not self._current:
-            # No pending request, remove timeout
-            self.setTimeout(None)
-
-
-    def increment(self, key, val=1):
-        """
-        Increment the value of C{key} by given value (default to 1).
-        C{key} must be consistent with an int. Return the new value.
-
-        @param key: the key to modify.
-        @type key: C{str}
-
-        @param val: the value to increment.
-        @type val: C{int}
-
-        @return: a deferred with will be called back with the new value
-            associated with the key (after the increment).
-        @rtype: L{Deferred}
-        """
-        return self._incrdecr("incr", key, val)
-
-
-    def decrement(self, key, val=1):
-        """
-        Decrement the value of C{key} by given value (default to 1).
-        C{key} must be consistent with an int. Return the new value, coerced to
-        0 if negative.
-
-        @param key: the key to modify.
-        @type key: C{str}
-
-        @param val: the value to decrement.
-        @type val: C{int}
-
-        @return: a deferred with will be called back with the new value
-            associated with the key (after the decrement).
-        @rtype: L{Deferred}
-        """
-        return self._incrdecr("decr", key, val)
-
-
-    def _incrdecr(self, cmd, key, val):
-        """
-        Internal wrapper for incr/decr.
-        """
-        if not isinstance(key, str):
-            return fail(ClientError(
-                "Invalid type for key: %s, expecting a string" % (type(key),)))
-        if len(key) > self.MAX_KEY_LENGTH:
-            return fail(ClientError("Key too long"))
-        fullcmd = "%s %s %d" % (cmd, key, int(val))
-        self.sendLine(fullcmd)
-        cmdObj = Command(cmd, key=key)
-        self._current.append(cmdObj)
-        return cmdObj._deferred
-
-
-    def replace(self, key, val, flags=0, expireTime=0):
-        """
-        Replace the given C{key}. It must already exist in the server.
-
-        @param key: the key to replace.
-        @type key: C{str}
-
-        @param val: the new value associated with the key.
-        @type val: C{str}
-
-        @param flags: the flags to store with the key.
-        @type flags: C{int}
-
-        @param expireTime: if different from 0, the relative time in seconds
-            when the key will be deleted from the store.
-        @type expireTime: C{int}
-
-        @return: a deferred that will fire with C{True} if the operation has
-            succeeded, and C{False} with the key didn't previously exist.
-        @rtype: L{Deferred}
-        """
-        return self._set("replace", key, val, flags, expireTime, "")
-
-
-    def add(self, key, val, flags=0, expireTime=0):
-        """
-        Add the given C{key}. It must not exist in the server.
-
-        @param key: the key to add.
-        @type key: C{str}
-
-        @param val: the value associated with the key.
-        @type val: C{str}
-
-        @param flags: the flags to store with the key.
-        @type flags: C{int}
-
-        @param expireTime: if different from 0, the relative time in seconds
-            when the key will be deleted from the store.
-        @type expireTime: C{int}
-
-        @return: a deferred that will fire with C{True} if the operation has
-            succeeded, and C{False} with the key already exists.
-        @rtype: L{Deferred}
-        """
-        return self._set("add", key, val, flags, expireTime, "")
-
-
-    def set(self, key, val, flags=0, expireTime=0):
-        """
-        Set the given C{key}.
-
-        @param key: the key to set.
-        @type key: C{str}
-
-        @param val: the value associated with the key.
-        @type val: C{str}
-
-        @param flags: the flags to store with the key.
-        @type flags: C{int}
-
-        @param expireTime: if different from 0, the relative time in seconds
-            when the key will be deleted from the store.
-        @type expireTime: C{int}
-
-        @return: a deferred that will fire with C{True} if the operation has
-            succeeded.
-        @rtype: L{Deferred}
-        """
-        return self._set("set", key, val, flags, expireTime, "")
-
-
-    def checkAndSet(self, key, val, cas, flags=0, expireTime=0):
-        """
-        Change the content of C{key} only if the C{cas} value matches the
-        current one associated with the key. Use this to store a value which
-        hasn't been modified since last time you fetched it.
-
-        @param key: The key to set.
-        @type key: C{str}
-
-        @param val: The value associated with the key.
-        @type val: C{str}
-
-        @param cas: Unique 64-bit value returned by previous call of C{get}.
-        @type cas: C{str}
-
-        @param flags: The flags to store with the key.
-        @type flags: C{int}
-
-        @param expireTime: If different from 0, the relative time in seconds
-            when the key will be deleted from the store.
-        @type expireTime: C{int}
-
-        @return: A deferred that will fire with C{True} if the operation has
-            succeeded, C{False} otherwise.
-        @rtype: L{Deferred}
-        """
-        return self._set("cas", key, val, flags, expireTime, cas)
-
-
-    def _set(self, cmd, key, val, flags, expireTime, cas):
-        """
-        Internal wrapper for setting values.
-        """
-        if not isinstance(key, str):
-            return fail(ClientError(
-                "Invalid type for key: %s, expecting a string" % (type(key),)))
-        if len(key) > self.MAX_KEY_LENGTH:
-            return fail(ClientError("Key too long"))
-        if not isinstance(val, str):
-            return fail(ClientError(
-                "Invalid type for value: %s, expecting a string" %
-                (type(val),)))
-        if cas:
-            cas = " " + cas
-        length = len(val)
-        fullcmd = "%s %s %d %d %d%s" % (
-            cmd, key, flags, expireTime, length, cas)
-        self.sendLine(fullcmd)
-        self.sendLine(val)
-        cmdObj = Command(cmd, key=key, flags=flags, length=length)
-        self._current.append(cmdObj)
-        return cmdObj._deferred
-
-
-    def append(self, key, val):
-        """
-        Append given data to the value of an existing key.
-
-        @param key: The key to modify.
-        @type key: C{str}
-
-        @param val: The value to append to the current value associated with
-            the key.
-        @type val: C{str}
-
-        @return: A deferred that will fire with C{True} if the operation has
-            succeeded, C{False} otherwise.
-        @rtype: L{Deferred}
-        """
-        # Even if flags and expTime values are ignored, we have to pass them
-        return self._set("append", key, val, 0, 0, "")
-
-
-    def prepend(self, key, val):
-        """
-        Prepend given data to the value of an existing key.
-
-        @param key: The key to modify.
-        @type key: C{str}
-
-        @param val: The value to prepend to the current value associated with
-            the key.
-        @type val: C{str}
-
-        @return: A deferred that will fire with C{True} if the operation has
-            succeeded, C{False} otherwise.
-        @rtype: L{Deferred}
-        """
-        # Even if flags and expTime values are ignored, we have to pass them
-        return self._set("prepend", key, val, 0, 0, "")
-
-
-    def get(self, key, withIdentifier=False):
-        """
-        Get the given C{key}. It doesn't support multiple keys. If
-        C{withIdentifier} is set to C{True}, the command issued is a C{gets},
-        that will return the current identifier associated with the value. This
-        identifier has to be used when issuing C{checkAndSet} update later,
-        using the corresponding method.
-
-        @param key: The key to retrieve.
-        @type key: C{str}
-
-        @param withIdentifier: If set to C{True}, retrieve the current
-            identifier along with the value and the flags.
-        @type withIdentifier: C{bool}
-
-        @return: A deferred that will fire with the tuple (flags, value) if
-            C{withIdentifier} is C{False}, or (flags, cas identifier, value)
-            if C{True}.
-        @rtype: L{Deferred}
-        """
-        if not isinstance(key, str):
-            return fail(ClientError(
-                "Invalid type for key: %s, expecting a string" % (type(key),)))
-        if len(key) > self.MAX_KEY_LENGTH:
-            return fail(ClientError("Key too long"))
-        if withIdentifier:
-            cmd = "gets"
-        else:
-            cmd = "get"
-        fullcmd = "%s %s" % (cmd, key)
-        self.sendLine(fullcmd)
-        cmdObj = Command(cmd, key=key, value=None, flags=0, cas="")
-        self._current.append(cmdObj)
-        return cmdObj._deferred
-
-
-    def stats(self, arg=None):
-        """
-        Get some stats from the server. It will be available as a dict.
-
-        @param arg: An optional additional string which will be sent along
-            with the I{stats} command.  The interpretation of this value by
-            the server is left undefined by the memcache protocol
-            specification.
-        @type arg: L{NoneType} or L{str}
-
-        @return: a deferred that will fire with a C{dict} of the available
-            statistics.
-        @rtype: L{Deferred}
-        """
-        cmd = "stats"
-        if arg:
-            cmd = "stats " + arg
-        self.sendLine(cmd)
-        cmdObj = Command("stats", values={})
-        self._current.append(cmdObj)
-        return cmdObj._deferred
-
-
-    def version(self):
-        """
-        Get the version of the server.
-
-        @return: a deferred that will fire with the string value of the
-            version.
-        @rtype: L{Deferred}
-        """
-        self.sendLine("version")
-        cmdObj = Command("version")
-        self._current.append(cmdObj)
-        return cmdObj._deferred
-
-
-    def delete(self, key):
-        """
-        Delete an existing C{key}.
-
-        @param key: the key to delete.
-        @type key: C{str}
-
-        @return: a deferred that will be called back with C{True} if the key
-            was successfully deleted, or C{False} if not.
-        @rtype: L{Deferred}
-        """
-        if not isinstance(key, str):
-            return fail(ClientError(
-                "Invalid type for key: %s, expecting a string" % (type(key),)))
-        self.sendLine("delete %s" % key)
-        cmdObj = Command("delete", key=key)
-        self._current.append(cmdObj)
-        return cmdObj._deferred
-
-
-    def flushAll(self):
-        """
-        Flush all cached values.
-
-        @return: a deferred that will be called back with C{True} when the
-            operation has succeeded.
-        @rtype: L{Deferred}
-        """
-        self.sendLine("flush_all")
-        cmdObj = Command("flush_all")
-        self._current.append(cmdObj)
-        return cmdObj._deferred
-
-
-
-__all__ = ["MemCacheProtocol", "DEFAULT_PORT", "NoSuchCommand", "ClientError",
-           "ServerError"]
-

Modified: CalendarServer/trunk/twistedcaldav/memcachepool.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/memcachepool.py	2009-03-11 23:33:58 UTC (rev 3830)
+++ CalendarServer/trunk/twistedcaldav/memcachepool.py	2009-03-11 23:34:25 UTC (rev 3831)
@@ -18,8 +18,9 @@
 from twisted.internet.defer import Deferred, fail
 from twisted.internet.protocol import ReconnectingClientFactory
 
+from twext.protocols.memcache import MemCacheProtocol, NoSuchCommand
+
 from twistedcaldav.log import LoggingMixIn
-from twistedcaldav.memcache import MemCacheProtocol, NoSuchCommand
 
 
 class PooledMemCacheProtocol(MemCacheProtocol):

Deleted: CalendarServer/trunk/twistedcaldav/test/test_memcache.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_memcache.py	2009-03-11 23:33:58 UTC (rev 3830)
+++ CalendarServer/trunk/twistedcaldav/test/test_memcache.py	2009-03-11 23:34:25 UTC (rev 3831)
@@ -1,523 +0,0 @@
-# Copyright (c) 2007-2009 Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-"""
-Test the memcache client protocol.
-"""
-
-from twisted.protocols.memcache import MemCacheProtocol, NoSuchCommand
-from twisted.protocols.memcache import ClientError, ServerError
-
-from twisted.trial.unittest import TestCase
-from twisted.test.proto_helpers import StringTransportWithDisconnection
-from twisted.internet.task import Clock
-from twisted.internet.defer import Deferred, gatherResults, TimeoutError
-
-
-
-class MemCacheTestCase(TestCase):
-    """
-    Test client protocol class L{MemCacheProtocol}.
-    """
-
-    def setUp(self):
-        """
-        Create a memcache client, connect it to a string protocol, and make it
-        use a deterministic clock.
-        """
-        self.proto = MemCacheProtocol()
-        self.clock = Clock()
-        self.proto.callLater = self.clock.callLater
-        self.transport = StringTransportWithDisconnection()
-        self.transport.protocol = self.proto
-        self.proto.makeConnection(self.transport)
-
-
-    def _test(self, d, send, recv, result):
-        """
-        Shortcut method for classic tests.
-
-        @param d: the resulting deferred from the memcache command.
-        @type d: C{Deferred}
-
-        @param send: the expected data to be sent.
-        @type send: C{str}
-
-        @param recv: the data to simulate as reception.
-        @type recv: C{str}
-
-        @param result: the expected result.
-        @type result: C{any}
-        """
-        def cb(res):
-            self.assertEquals(res, result)
-        self.assertEquals(self.transport.value(), send)
-        d.addCallback(cb)
-        self.proto.dataReceived(recv)
-        return d
-
-
-    def test_get(self):
-        """
-        L{MemCacheProtocol.get} should return a L{Deferred} which is
-        called back with the value and the flag associated with the given key
-        if the server returns a successful result.
-        """
-        return self._test(self.proto.get("foo"), "get foo\r\n",
-            "VALUE foo 0 3\r\nbar\r\nEND\r\n", (0, "bar"))
-
-
-    def test_emptyGet(self):
-        """
-        Test getting a non-available key: it should succeed but return C{None}
-        as value and C{0} as flag.
-        """
-        return self._test(self.proto.get("foo"), "get foo\r\n",
-            "END\r\n", (0, None))
-
-
-    def test_set(self):
-        """
-        L{MemCacheProtocol.set} should return a L{Deferred} which is
-        called back with C{True} when the operation succeeds.
-        """
-        return self._test(self.proto.set("foo", "bar"),
-            "set foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
-
-
-    def test_add(self):
-        """
-        L{MemCacheProtocol.add} should return a L{Deferred} which is
-        called back with C{True} when the operation succeeds.
-        """
-        return self._test(self.proto.add("foo", "bar"),
-            "add foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
-
-
-    def test_replace(self):
-        """
-        L{MemCacheProtocol.replace} should return a L{Deferred} which
-        is called back with C{True} when the operation succeeds.
-        """
-        return self._test(self.proto.replace("foo", "bar"),
-            "replace foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
-
-
-    def test_errorAdd(self):
-        """
-        Test an erroneous add: if a L{MemCacheProtocol.add} is called but the
-        key already exists on the server, it returns a B{NOT STORED} answer,
-        which should callback the resulting L{Deferred} with C{False}.
-        """
-        return self._test(self.proto.add("foo", "bar"),
-            "add foo 0 0 3\r\nbar\r\n", "NOT STORED\r\n", False)
-
-
-    def test_errorReplace(self):
-        """
-        Test an erroneous replace: if a L{MemCacheProtocol.replace} is called
-        but the key doesn't exist on the server, it returns a B{NOT STORED}
-        answer, which should callback the resulting L{Deferred} with C{False}.
-        """
-        return self._test(self.proto.replace("foo", "bar"),
-            "replace foo 0 0 3\r\nbar\r\n", "NOT STORED\r\n", False)
-
-
-    def test_delete(self):
-        """
-        L{MemCacheProtocol.delete} should return a L{Deferred} which is
-        called back with C{True} when the server notifies a success.
-        """
-        return self._test(self.proto.delete("bar"), "delete bar\r\n",
-            "DELETED\r\n", True)
-
-
-    def test_errorDelete(self):
-        """
-        Test a error during a delete: if key doesn't exist on the server, it
-        returns a B{NOT FOUND} answer which should callback the resulting
-        L{Deferred} with C{False}.
-        """
-        return self._test(self.proto.delete("bar"), "delete bar\r\n",
-            "NOT FOUND\r\n", False)
-
-
-    def test_increment(self):
-        """
-        Test incrementing a variable: L{MemCacheProtocol.increment} should
-        return a L{Deferred} which is called back with the incremented value of
-        the given key.
-        """
-        return self._test(self.proto.increment("foo"), "incr foo 1\r\n",
-            "4\r\n", 4)
-
-
-    def test_decrement(self):
-        """
-        Test decrementing a variable: L{MemCacheProtocol.decrement} should
-        return a L{Deferred} which is called back with the decremented value of
-        the given key.
-        """
-        return self._test(
-            self.proto.decrement("foo"), "decr foo 1\r\n", "5\r\n", 5)
-
-
-    def test_incrementVal(self):
-        """
-        L{MemCacheProtocol.increment} takes an optional argument C{value} which
-        should replace the default value of 1 when specified.
-        """
-        return self._test(self.proto.increment("foo", 8), "incr foo 8\r\n",
-            "4\r\n", 4)
-
-
-    def test_decrementVal(self):
-        """
-        L{MemCacheProtocol.decrement} takes an optional argument C{value} which
-        should replace the default value of 1 when specified.
-        """
-        return self._test(self.proto.decrement("foo", 3), "decr foo 3\r\n",
-            "5\r\n", 5)
-
-
-    def test_stats(self):
-        """
-        Test retrieving server statistics via the L{MemCacheProtocol.stats}
-        command: it should parse the data sent by the server and call back the
-        resulting L{Deferred} with a dictionary of the received statistics.
-        """
-        return self._test(self.proto.stats(), "stats\r\n",
-            "STAT foo bar\r\nSTAT egg spam\r\nEND\r\n",
-            {"foo": "bar", "egg": "spam"})
-
-
-    def test_statsWithArgument(self):
-        """
-
-        L{MemCacheProtocol.stats} takes an optional C{str} argument which,
-        if specified, is sent along with the I{STAT} command.  The I{STAT}
-        responses from the server are parsed as key/value pairs and returned
-        as a C{dict} (as in the case where the argument is not specified).
-        """
-        return self._test(self.proto.stats("blah"), "stats blah\r\n",
-            "STAT foo bar\r\nSTAT egg spam\r\nEND\r\n",
-            {"foo": "bar", "egg": "spam"})
-
-
-    def test_version(self):
-        """
-        Test version retrieval via the L{MemCacheProtocol.version} command: it
-        should return a L{Deferred} which is called back with the version sent
-        by the server.
-        """
-        return self._test(self.proto.version(), "version\r\n",
-            "VERSION 1.1\r\n", "1.1")
-
-
-    def test_flushAll(self):
-        """
-        L{MemCacheProtocol.flushAll} should return a L{Deferred} which is
-        called back with C{True} if the server acknowledges success.
-        """
-        return self._test(self.proto.flushAll(), "flush_all\r\n",
-            "OK\r\n", True)
-
-
-    def test_invalidGetResponse(self):
-        """
-        If the value returned doesn't match the expected key of the current, we
-        should get an error in L{MemCacheProtocol.dataReceived}.
-        """
-        self.proto.get("foo")
-        s = "spamegg"
-        self.assertRaises(RuntimeError,
-            self.proto.dataReceived,
-            "VALUE bar 0 %s\r\n%s\r\nEND\r\n" % (len(s), s))
-
-
-    def test_timeOut(self):
-        """
-        Test the timeout on outgoing requests: when timeout is detected, all
-        current commands should fail with a L{TimeoutError}, and the
-        connection should be closed.
-        """
-        d1 = self.proto.get("foo")
-        d2 = self.proto.get("bar")
-        d3 = Deferred()
-        self.proto.connectionLost = d3.callback
-
-        self.clock.advance(self.proto.persistentTimeOut)
-        self.assertFailure(d1, TimeoutError)
-        self.assertFailure(d2, TimeoutError)
-        def checkMessage(error):
-            self.assertEquals(str(error), "Connection timeout")
-        d1.addCallback(checkMessage)
-        return gatherResults([d1, d2, d3])
-
-
-    def test_timeoutRemoved(self):
-        """
-        When a request gets a response, no pending timeout call should remain
-        around.
-        """
-        d = self.proto.get("foo")
-
-        self.clock.advance(self.proto.persistentTimeOut - 1)
-        self.proto.dataReceived("VALUE foo 0 3\r\nbar\r\nEND\r\n")
-
-        def check(result):
-            self.assertEquals(result, (0, "bar"))
-            self.assertEquals(len(self.clock.calls), 0)
-        d.addCallback(check)
-        return d
-
-
-    def test_timeOutRaw(self):
-        """
-        Test the timeout when raw mode was started: the timeout should not be
-        reset until all the data has been received, so we can have a
-        L{TimeoutError} when waiting for raw data.
-        """
-        d1 = self.proto.get("foo")
-        d2 = Deferred()
-        self.proto.connectionLost = d2.callback
-
-        self.proto.dataReceived("VALUE foo 0 10\r\n12345")
-        self.clock.advance(self.proto.persistentTimeOut)
-        self.assertFailure(d1, TimeoutError)
-        return gatherResults([d1, d2])
-
-
-    def test_timeOutStat(self):
-        """
-        Test the timeout when stat command has started: the timeout should not
-        be reset until the final B{END} is received.
-        """
-        d1 = self.proto.stats()
-        d2 = Deferred()
-        self.proto.connectionLost = d2.callback
-
-        self.proto.dataReceived("STAT foo bar\r\n")
-        self.clock.advance(self.proto.persistentTimeOut)
-        self.assertFailure(d1, TimeoutError)
-        return gatherResults([d1, d2])
-
-
-    def test_timeoutPipelining(self):
-        """
-        When two requests are sent, a timeout call should remain around for the
-        second request, and its timeout time should be correct.
-        """
-        d1 = self.proto.get("foo")
-        d2 = self.proto.get("bar")
-        d3 = Deferred()
-        self.proto.connectionLost = d3.callback
-
-        self.clock.advance(self.proto.persistentTimeOut - 1)
-        self.proto.dataReceived("VALUE foo 0 3\r\nbar\r\nEND\r\n")
-
-        def check(result):
-            self.assertEquals(result, (0, "bar"))
-            self.assertEquals(len(self.clock.calls), 1)
-            for i in range(self.proto.persistentTimeOut):
-                self.clock.advance(1)
-            return self.assertFailure(d2, TimeoutError).addCallback(checkTime)
-        def checkTime(ignored):
-            # Check that the timeout happened C{self.proto.persistentTimeOut}
-            # after the last response
-            self.assertEquals(self.clock.seconds(),
-                    2 * self.proto.persistentTimeOut - 1)
-        d1.addCallback(check)
-        return d1
-
-
-    def test_timeoutNotReset(self):
-        """
-        Check that timeout is not resetted for every command, but keep the
-        timeout from the first command without response.
-        """
-        d1 = self.proto.get("foo")
-        d3 = Deferred()
-        self.proto.connectionLost = d3.callback
-
-        self.clock.advance(self.proto.persistentTimeOut - 1)
-        d2 = self.proto.get("bar")
-        self.clock.advance(1)
-        self.assertFailure(d1, TimeoutError)
-        self.assertFailure(d2, TimeoutError)
-        return gatherResults([d1, d2, d3])
-
-
-    def test_tooLongKey(self):
-        """
-        Test that an error is raised when trying to use a too long key: the
-        called command should return a L{Deferred} which fail with a
-        L{ClientError}.
-        """
-        d1 = self.assertFailure(self.proto.set("a" * 500, "bar"), ClientError)
-        d2 = self.assertFailure(self.proto.increment("a" * 500), ClientError)
-        d3 = self.assertFailure(self.proto.get("a" * 500), ClientError)
-        d4 = self.assertFailure(self.proto.append("a" * 500, "bar"), ClientError)
-        d5 = self.assertFailure(self.proto.prepend("a" * 500, "bar"), ClientError)
-        return gatherResults([d1, d2, d3, d4, d5])
-
-
-    def test_invalidCommand(self):
-        """
-        When an unknown command is sent directly (not through public API), the
-        server answers with an B{ERROR} token, and the command should fail with
-        L{NoSuchCommand}.
-        """
-        d = self.proto._set("egg", "foo", "bar", 0, 0, "")
-        self.assertEquals(self.transport.value(), "egg foo 0 0 3\r\nbar\r\n")
-        self.assertFailure(d, NoSuchCommand)
-        self.proto.dataReceived("ERROR\r\n")
-        return d
-
-
-    def test_clientError(self):
-        """
-        Test the L{ClientError} error: when the server send a B{CLIENT_ERROR}
-        token, the originating command should fail with L{ClientError}, and the
-        error should contain the text sent by the server.
-        """
-        a = "eggspamm"
-        d = self.proto.set("foo", a)
-        self.assertEquals(self.transport.value(),
-                          "set foo 0 0 8\r\neggspamm\r\n")
-        self.assertFailure(d, ClientError)
-        def check(err):
-            self.assertEquals(str(err), "We don't like egg and spam")
-        d.addCallback(check)
-        self.proto.dataReceived("CLIENT_ERROR We don't like egg and spam\r\n")
-        return d
-
-
-    def test_serverError(self):
-        """
-        Test the L{ServerError} error: when the server send a B{SERVER_ERROR}
-        token, the originating command should fail with L{ServerError}, and the
-        error should contain the text sent by the server.
-        """
-        a = "eggspamm"
-        d = self.proto.set("foo", a)
-        self.assertEquals(self.transport.value(),
-                          "set foo 0 0 8\r\neggspamm\r\n")
-        self.assertFailure(d, ServerError)
-        def check(err):
-            self.assertEquals(str(err), "zomg")
-        d.addCallback(check)
-        self.proto.dataReceived("SERVER_ERROR zomg\r\n")
-        return d
-
-
-    def test_unicodeKey(self):
-        """
-        Using a non-string key as argument to commands should raise an error.
-        """
-        d1 = self.assertFailure(self.proto.set(u"foo", "bar"), ClientError)
-        d2 = self.assertFailure(self.proto.increment(u"egg"), ClientError)
-        d3 = self.assertFailure(self.proto.get(1), ClientError)
-        d4 = self.assertFailure(self.proto.delete(u"bar"), ClientError)
-        d5 = self.assertFailure(self.proto.append(u"foo", "bar"), ClientError)
-        d6 = self.assertFailure(self.proto.prepend(u"foo", "bar"), ClientError)
-        return gatherResults([d1, d2, d3, d4, d5, d6])
-
-
-    def test_unicodeValue(self):
-        """
-        Using a non-string value should raise an error.
-        """
-        return self.assertFailure(self.proto.set("foo", u"bar"), ClientError)
-
-
-    def test_pipelining(self):
-        """
-        Test that multiple requests can be sent subsequently to the server, and
-        that the protocol order the responses correctly and dispatch to the
-        corresponding client command.
-        """
-        d1 = self.proto.get("foo")
-        d1.addCallback(self.assertEquals, (0, "bar"))
-        d2 = self.proto.set("bar", "spamspamspam")
-        d2.addCallback(self.assertEquals, True)
-        d3 = self.proto.get("egg")
-        d3.addCallback(self.assertEquals, (0, "spam"))
-        self.assertEquals(self.transport.value(),
-            "get foo\r\nset bar 0 0 12\r\nspamspamspam\r\nget egg\r\n")
-        self.proto.dataReceived("VALUE foo 0 3\r\nbar\r\nEND\r\n"
-                                "STORED\r\n"
-                                "VALUE egg 0 4\r\nspam\r\nEND\r\n")
-        return gatherResults([d1, d2, d3])
-
-
-    def test_getInChunks(self):
-        """
-        If the value retrieved by a C{get} arrive in chunks, the protocol
-        should be able to reconstruct it and to produce the good value.
-        """
-        d = self.proto.get("foo")
-        d.addCallback(self.assertEquals, (0, "0123456789"))
-        self.assertEquals(self.transport.value(), "get foo\r\n")
-        self.proto.dataReceived("VALUE foo 0 10\r\n0123456")
-        self.proto.dataReceived("789")
-        self.proto.dataReceived("\r\nEND")
-        self.proto.dataReceived("\r\n")
-        return d
-
-
-    def test_append(self):
-        """
-        L{MemCacheProtocol.append} behaves like a L{MemCacheProtocol.set}
-        method: it should return a L{Deferred} which is called back with
-        C{True} when the operation succeeds.
-        """
-        return self._test(self.proto.append("foo", "bar"),
-            "append foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
-
-
-    def test_prepend(self):
-        """
-        L{MemCacheProtocol.prepend} behaves like a L{MemCacheProtocol.set}
-        method: it should return a L{Deferred} which is called back with
-        C{True} when the operation succeeds.
-        """
-        return self._test(self.proto.prepend("foo", "bar"),
-            "prepend foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
-
-
-    def test_gets(self):
-        """
-        L{MemCacheProtocol.get} should handle an additional cas result when
-        C{withIdentifier} is C{True} and forward it in the resulting
-        L{Deferred}.
-        """
-        return self._test(self.proto.get("foo", True), "gets foo\r\n",
-            "VALUE foo 0 3 1234\r\nbar\r\nEND\r\n", (0, "1234", "bar"))
-
-
-    def test_emptyGets(self):
-        """
-        Test getting a non-available key with gets: it should succeed but
-        return C{None} as value, C{0} as flag and an empty cas value.
-        """
-        return self._test(self.proto.get("foo", True), "gets foo\r\n",
-            "END\r\n", (0, "", None))
-
-
-    def test_checkAndSet(self):
-        """
-        L{MemCacheProtocol.checkAndSet} passes an additional cas identifier that the
-        server should handle to check if the data has to be updated.
-        """
-        return self._test(self.proto.checkAndSet("foo", "bar", cas="1234"),
-            "cas foo 0 0 3 1234\r\nbar\r\n", "STORED\r\n", True)
-
-
-    def test_casUnknowKey(self):
-        """
-        When L{MemCacheProtocol.checkAndSet} response is C{EXISTS}, the resulting
-        L{Deferred} should fire with C{False}.
-        """
-        return self._test(self.proto.checkAndSet("foo", "bar", cas="1234"),
-            "cas foo 0 0 3 1234\r\nbar\r\n", "EXISTS\r\n", False)

Modified: CalendarServer/trunk/twistedcaldav/test/test_memcachelock.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_memcachelock.py	2009-03-11 23:33:58 UTC (rev 3830)
+++ CalendarServer/trunk/twistedcaldav/test/test_memcachelock.py	2009-03-11 23:34:25 UTC (rev 3831)
@@ -5,13 +5,14 @@
 Test the memcache client protocol.
 """
 
-from twistedcaldav.memcache import MemCacheProtocol
-from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
+from twext.protocols.memcache import MemCacheProtocol
 
 from twisted.test.proto_helpers import StringTransportWithDisconnection
 from twisted.internet.task import Clock
 from twisted.internet.defer import inlineCallbacks
 
+from twistedcaldav.memcachelock import MemcacheLock, MemcacheLockTimeoutError
+
 from twistedcaldav.test.util import TestCase
 
 class MemCacheTestCase(TestCase):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090311/06091f96/attachment-0001.html>


More information about the calendarserver-changes mailing list