1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
5 Tests for L{twisted.internet.protocol}.
8 from zope.interface.verify import verifyObject
10 from twisted.python.failure import Failure
11 from twisted.internet.interfaces import IProtocol, ILoggingContext
12 from twisted.internet.defer import CancelledError
13 from twisted.internet.protocol import Protocol, ClientCreator
14 from twisted.internet.task import Clock
15 from twisted.trial.unittest import TestCase
16 from twisted.test.proto_helpers import MemoryReactor, StringTransport
20 class MemoryConnector:
24 self._disconnected = True
28 class MemoryReactorWithConnectorsAndTime(MemoryReactor, Clock):
30 An extension of L{MemoryReactor} which returns L{IConnector}
31 providers from its C{connectTCP} method.
34 MemoryReactor.__init__(self)
39 def connectTCP(self, *a, **kw):
40 MemoryReactor.connectTCP(self, *a, **kw)
41 connector = MemoryConnector()
42 self.connectors.append(connector)
46 def connectUNIX(self, *a, **kw):
47 MemoryReactor.connectUNIX(self, *a, **kw)
48 connector = MemoryConnector()
49 self.connectors.append(connector)
53 def connectSSL(self, *a, **kw):
54 MemoryReactor.connectSSL(self, *a, **kw)
55 connector = MemoryConnector()
56 self.connectors.append(connector)
61 class ClientCreatorTests(TestCase):
63 Tests for L{twisted.internet.protocol.ClientCreator}.
65 def _basicConnectTest(self, check):
67 Helper for implementing a test to verify that one of the I{connect}
68 methods of L{ClientCreator} passes the right arguments to the right
71 @param check: A function which will be invoked with a reactor and a
72 L{ClientCreator} instance and which should call one of the
73 L{ClientCreator}'s I{connect} methods and assert that all of its
74 arguments except for the factory are passed on as expected to the
75 reactor. The factory should be returned.
77 class SomeProtocol(Protocol):
80 reactor = MemoryReactorWithConnectorsAndTime()
81 cc = ClientCreator(reactor, SomeProtocol)
82 factory = check(reactor, cc)
83 protocol = factory.buildProtocol(None)
84 self.assertIsInstance(protocol, SomeProtocol)
87 def test_connectTCP(self):
89 L{ClientCreator.connectTCP} calls C{reactor.connectTCP} with the host
90 and port information passed to it, and with a factory which will
91 construct the protocol passed to L{ClientCreator.__init__}.
93 def check(reactor, cc):
94 cc.connectTCP('example.com', 1234, 4321, ('1.2.3.4', 9876))
95 host, port, factory, timeout, bindAddress = reactor.tcpClients.pop()
96 self.assertEqual(host, 'example.com')
97 self.assertEqual(port, 1234)
98 self.assertEqual(timeout, 4321)
99 self.assertEqual(bindAddress, ('1.2.3.4', 9876))
101 self._basicConnectTest(check)
104 def test_connectUNIX(self):
106 L{ClientCreator.connectUNIX} calls C{reactor.connectUNIX} with the
107 filename passed to it, and with a factory which will construct the
108 protocol passed to L{ClientCreator.__init__}.
110 def check(reactor, cc):
111 cc.connectUNIX('/foo/bar', 123, True)
112 address, factory, timeout, checkPID = reactor.unixClients.pop()
113 self.assertEqual(address, '/foo/bar')
114 self.assertEqual(timeout, 123)
115 self.assertEqual(checkPID, True)
117 self._basicConnectTest(check)
120 def test_connectSSL(self):
122 L{ClientCreator.connectSSL} calls C{reactor.connectSSL} with the host,
123 port, and context factory passed to it, and with a factory which will
124 construct the protocol passed to L{ClientCreator.__init__}.
126 def check(reactor, cc):
127 expectedContextFactory = object()
128 cc.connectSSL('example.com', 1234, expectedContextFactory, 4321, ('4.3.2.1', 5678))
129 host, port, factory, contextFactory, timeout, bindAddress = reactor.sslClients.pop()
130 self.assertEqual(host, 'example.com')
131 self.assertEqual(port, 1234)
132 self.assertIdentical(contextFactory, expectedContextFactory)
133 self.assertEqual(timeout, 4321)
134 self.assertEqual(bindAddress, ('4.3.2.1', 5678))
136 self._basicConnectTest(check)
139 def _cancelConnectTest(self, connect):
141 Helper for implementing a test to verify that cancellation of the
142 L{Deferred} returned by one of L{ClientCreator}'s I{connect} methods is
143 implemented to cancel the underlying connector.
145 @param connect: A function which will be invoked with a L{ClientCreator}
146 instance as an argument and which should call one its I{connect}
147 methods and return the result.
149 @return: A L{Deferred} which fires when the test is complete or fails if
152 reactor = MemoryReactorWithConnectorsAndTime()
153 cc = ClientCreator(reactor, Protocol)
155 connector = reactor.connectors.pop()
156 self.assertFalse(connector._disconnected)
158 self.assertTrue(connector._disconnected)
159 return self.assertFailure(d, CancelledError)
162 def test_cancelConnectTCP(self):
164 The L{Deferred} returned by L{ClientCreator.connectTCP} can be cancelled
165 to abort the connection attempt before it completes.
168 return cc.connectTCP('example.com', 1234)
169 return self._cancelConnectTest(connect)
172 def test_cancelConnectUNIX(self):
174 The L{Deferred} returned by L{ClientCreator.connectTCP} can be cancelled
175 to abort the connection attempt before it completes.
178 return cc.connectUNIX('/foo/bar')
179 return self._cancelConnectTest(connect)
182 def test_cancelConnectSSL(self):
184 The L{Deferred} returned by L{ClientCreator.connectTCP} can be cancelled
185 to abort the connection attempt before it completes.
188 return cc.connectSSL('example.com', 1234, object())
189 return self._cancelConnectTest(connect)
192 def _cancelConnectTimeoutTest(self, connect):
194 Like L{_cancelConnectTest}, but for the case where the L{Deferred} is
195 cancelled after the connection is set up but before it is fired with the
196 resulting protocol instance.
198 reactor = MemoryReactorWithConnectorsAndTime()
199 cc = ClientCreator(reactor, Protocol)
200 d = connect(reactor, cc)
201 connector = reactor.connectors.pop()
202 # Sanity check - there is an outstanding delayed call to fire the
204 self.assertEqual(len(reactor.getDelayedCalls()), 1)
206 # Cancel the Deferred, disconnecting the transport just set up and
207 # cancelling the delayed call.
210 self.assertEqual(reactor.getDelayedCalls(), [])
212 # A real connector implementation is responsible for disconnecting the
213 # transport as well. For our purposes, just check that someone told the
214 # connector to disconnect.
215 self.assertTrue(connector._disconnected)
217 return self.assertFailure(d, CancelledError)
220 def test_cancelConnectTCPTimeout(self):
222 L{ClientCreator.connectTCP} inserts a very short delayed call between
223 the time the connection is established and the time the L{Deferred}
224 returned from one of its connect methods actually fires. If the
225 L{Deferred} is cancelled in this interval, the established connection is
226 closed, the timeout is cancelled, and the L{Deferred} fails with
229 def connect(reactor, cc):
230 d = cc.connectTCP('example.com', 1234)
231 host, port, factory, timeout, bindAddress = reactor.tcpClients.pop()
232 protocol = factory.buildProtocol(None)
233 transport = StringTransport()
234 protocol.makeConnection(transport)
236 return self._cancelConnectTimeoutTest(connect)
239 def test_cancelConnectUNIXTimeout(self):
241 L{ClientCreator.connectUNIX} inserts a very short delayed call between
242 the time the connection is established and the time the L{Deferred}
243 returned from one of its connect methods actually fires. If the
244 L{Deferred} is cancelled in this interval, the established connection is
245 closed, the timeout is cancelled, and the L{Deferred} fails with
248 def connect(reactor, cc):
249 d = cc.connectUNIX('/foo/bar')
250 address, factory, timeout, bindAddress = reactor.unixClients.pop()
251 protocol = factory.buildProtocol(None)
252 transport = StringTransport()
253 protocol.makeConnection(transport)
255 return self._cancelConnectTimeoutTest(connect)
258 def test_cancelConnectSSLTimeout(self):
260 L{ClientCreator.connectSSL} inserts a very short delayed call between
261 the time the connection is established and the time the L{Deferred}
262 returned from one of its connect methods actually fires. If the
263 L{Deferred} is cancelled in this interval, the established connection is
264 closed, the timeout is cancelled, and the L{Deferred} fails with
267 def connect(reactor, cc):
268 d = cc.connectSSL('example.com', 1234, object())
269 host, port, factory, contextFactory, timeout, bindADdress = reactor.sslClients.pop()
270 protocol = factory.buildProtocol(None)
271 transport = StringTransport()
272 protocol.makeConnection(transport)
274 return self._cancelConnectTimeoutTest(connect)
277 def _cancelConnectFailedTimeoutTest(self, connect):
279 Like L{_cancelConnectTest}, but for the case where the L{Deferred} is
280 cancelled after the connection attempt has failed but before it is fired
281 with the resulting failure.
283 reactor = MemoryReactorWithConnectorsAndTime()
284 cc = ClientCreator(reactor, Protocol)
285 d, factory = connect(reactor, cc)
286 connector = reactor.connectors.pop()
287 factory.clientConnectionFailed(
288 connector, Failure(Exception("Simulated failure")))
290 # Sanity check - there is an outstanding delayed call to fire the
292 self.assertEqual(len(reactor.getDelayedCalls()), 1)
294 # Cancel the Deferred, cancelling the delayed call.
297 self.assertEqual(reactor.getDelayedCalls(), [])
299 return self.assertFailure(d, CancelledError)
302 def test_cancelConnectTCPFailedTimeout(self):
304 Similar to L{test_cancelConnectTCPTimeout}, but for the case where the
305 connection attempt fails.
307 def connect(reactor, cc):
308 d = cc.connectTCP('example.com', 1234)
309 host, port, factory, timeout, bindAddress = reactor.tcpClients.pop()
311 return self._cancelConnectFailedTimeoutTest(connect)
314 def test_cancelConnectUNIXFailedTimeout(self):
316 Similar to L{test_cancelConnectUNIXTimeout}, but for the case where the
317 connection attempt fails.
319 def connect(reactor, cc):
320 d = cc.connectUNIX('/foo/bar')
321 address, factory, timeout, bindAddress = reactor.unixClients.pop()
323 return self._cancelConnectFailedTimeoutTest(connect)
326 def test_cancelConnectSSLFailedTimeout(self):
328 Similar to L{test_cancelConnectSSLTimeout}, but for the case where the
329 connection attempt fails.
331 def connect(reactor, cc):
332 d = cc.connectSSL('example.com', 1234, object())
333 host, port, factory, contextFactory, timeout, bindADdress = reactor.sslClients.pop()
335 return self._cancelConnectFailedTimeoutTest(connect)
338 class ProtocolTests(TestCase):
340 Tests for L{twisted.internet.protocol.Protocol}.
342 def test_interfaces(self):
344 L{Protocol} instances provide L{IProtocol} and L{ILoggingContext}.
347 self.assertTrue(verifyObject(IProtocol, proto))
348 self.assertTrue(verifyObject(ILoggingContext, proto))
351 def test_logPrefix(self):
353 L{Protocol.logPrefix} returns the protocol class's name.
355 class SomeThing(Protocol):
357 self.assertEqual("SomeThing", SomeThing().logPrefix())