[Twisted-Python] Ldaptor: [PATCH] Extend test driver send_multiResponse() to return deferred and throw errors

Anton Gyllenberg anton at iki.fi
Thu Sep 2 05:28:56 EDT 2010

The deferred returned by the LDAP client send_multiResponse() method was
previously unused in all code covered by tests, and so the replacement
method in the test driver just returned None. The deferred is now used
in search() and this change makes the test driver also return a deferred
instead of None in order to make things work when run within the test

To make it possible to test failures in the client send() and
send_multiResponse() methods, the test driver is changed to accept Failure
instances in place of lists of LDAPProtocolResponses.  Doing this causes
the errback on the deferred to be called with this failure.

The LDAPSyntaxSearch and Bind test cases are augmented with one test each
to use the new failure functionality in the client test driver. As the
search() code a while back did not handle errors in the send_multiResponse()
deferred chain the test case would time out if run against older code.
Therefore a smaller timeout of 3 seconds is set for the LDAPSyntaxSearch test

Discussion: With this change the old test cases pass and the code path
introduced by my modifications to send_multiResponse() is tested by a
new test case. I am still a bit unsure if I am testing the right thing
and if the original fix is the right thing to do. Any comments

Code published on http://github.com/antong/ldaptor/tree/pu

 ldaptor/test/test_ldapsyntax.py |   25 +++++++++++++++++++++++++
 ldaptor/testutil.py             |   18 +++++++++++++++++-
 2 files changed, 42 insertions(+), 1 deletions(-)

diff --git a/ldaptor/test/test_ldapsyntax.py b/ldaptor/test/test_ldapsyntax.py
index b8bcf53..46be06c 100755
--- a/ldaptor/test/test_ldapsyntax.py
+++ b/ldaptor/test/test_ldapsyntax.py
@@ -7,6 +7,7 @@ from ldaptor import config, testutil, delta
 from ldaptor.protocols.ldap import ldapsyntax, ldaperrors
 from ldaptor.protocols import pureldap, pureber
 from twisted.internet import defer
+from twisted.internet import error
 from twisted.python import failure
 from ldaptor.testutil import LDAPClientTestDriver

@@ -366,6 +367,7 @@ class

 class LDAPSyntaxSearch(unittest.TestCase):
+    timeout = 3
     def testSearch(self):
         """Test searches."""

@@ -641,6 +643,17 @@ class LDAPSyntaxSearch(unittest.TestCase):
         d.addCallbacks(testutil.mustRaise, eb)
         return d

+    def testSearch_err(self):
+        client=LDAPClientTestDriver([
+                failure.Failure(error.ConnectionLost())
+                ])
+        o = ldapsyntax.LDAPEntry(client=client, dn='dc=example,dc=com')
+        d = o.search(filterText='(foo=a)')
+        def eb(fail):
+            fail.trap(error.ConnectionLost)
+        d.addCallbacks(testutil.mustRaise, eb)
+        return d
 class LDAPSyntaxDNs(unittest.TestCase):
     def testDNKeyExistenceSuccess(self):
         client = LDAPClientTestDriver()
@@ -1516,3 +1529,15 @@ class Bind(unittest.TestCase):
         d.addCallbacks(testutil.mustRaise, eb)
         return d
+    def test_err(self):
+        client = LDAPClientTestDriver([
+                failure.Failure(error.ConnectionLost())])
+        o=ldapsyntax.LDAPEntry(client=client,
+                               dn='cn=foo,dc=example,dc=com')
+        d = defer.maybeDeferred(o.bind, 'whatever')
+        def eb(fail):
+            fail.trap(error.ConnectionLost)
+        d.addCallbacks(testutil.mustRaise, eb)
+        return d
diff --git a/ldaptor/testutil.py b/ldaptor/testutil.py
index 8307cb9..cb25aa3 100644
--- a/ldaptor/testutil.py
+++ b/ldaptor/testutil.py
@@ -1,6 +1,7 @@
 """Utilities for writing Twistedy unit tests and debugging."""

 from twisted.internet import defer
+from twisted.python import failure
 from twisted.trial import unittest
 from twisted.test import proto_helpers
 from ldaptor import config
@@ -36,23 +37,37 @@ class LDAPClientTestDriver:
     messages are stored in self.sent, so you can assert that the sent
     messages are what they are supposed to be.

+    It is also possible to include a Failure instance instead of a list
+    of LDAPProtocolResponses to cause which will cause the errback
+    to be called with the failure.
     def __init__(self, *responses):
         self.connected = None
         self.transport = FakeTransport(self)
     def send(self, op):
         l = self._response()
         assert len(l) == 1, \
                "got %d responses for a .send()" % len(l)
-        return defer.succeed(l[0])
+        r = l[0]
+        if isinstance(r, failure.Failure):
+            return defer.fail(r)
+        else:
+            return defer.succeed(r)
     def send_multiResponse(self, op, handler, *args, **kwargs):
+        d = defer.Deferred()
         responses = self._response()
         while responses:
             r = responses.pop(0)
+            if isinstance(r, failure.Failure):
+                d.errback(r)
+                break
             ret = handler(r, *args, **kwargs)
             if responses:
                 assert not ret, \
@@ -60,6 +75,7 @@ class LDAPClientTestDriver:
                 assert ret, \
                        "no more responses to give, but handler still
wants more (got %r)." % ret
+        return d

     def send_noResponse(self, op):
         responses = self.responses.pop(0)

More information about the Twisted-Python mailing list