test_posixbase.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for L{twisted.internet.posixbase} and supporting code.
  5. """
  6. from __future__ import division, absolute_import
  7. from twisted.trial.unittest import TestCase
  8. from twisted.internet.defer import Deferred
  9. from twisted.internet.posixbase import PosixReactorBase, _Waker
  10. from twisted.internet.protocol import ServerFactory
  11. skipSockets = None
  12. try:
  13. from twisted.internet import unix
  14. from twisted.test.test_unix import ClientProto
  15. except ImportError:
  16. skipSockets = "Platform does not support AF_UNIX sockets"
  17. from twisted.internet.tcp import Port
  18. from twisted.internet import reactor
  19. class TrivialReactor(PosixReactorBase):
  20. def __init__(self):
  21. self._readers = {}
  22. self._writers = {}
  23. PosixReactorBase.__init__(self)
  24. def addReader(self, reader):
  25. self._readers[reader] = True
  26. def removeReader(self, reader):
  27. del self._readers[reader]
  28. def addWriter(self, writer):
  29. self._writers[writer] = True
  30. def removeWriter(self, writer):
  31. del self._writers[writer]
  32. class PosixReactorBaseTests(TestCase):
  33. """
  34. Tests for L{PosixReactorBase}.
  35. """
  36. def _checkWaker(self, reactor):
  37. self.assertIsInstance(reactor.waker, _Waker)
  38. self.assertIn(reactor.waker, reactor._internalReaders)
  39. self.assertIn(reactor.waker, reactor._readers)
  40. def test_wakerIsInternalReader(self):
  41. """
  42. When L{PosixReactorBase} is instantiated, it creates a waker and adds
  43. it to its internal readers set.
  44. """
  45. reactor = TrivialReactor()
  46. self._checkWaker(reactor)
  47. def test_removeAllSkipsInternalReaders(self):
  48. """
  49. Any L{IReadDescriptors} in L{PosixReactorBase._internalReaders} are
  50. left alone by L{PosixReactorBase._removeAll}.
  51. """
  52. reactor = TrivialReactor()
  53. extra = object()
  54. reactor._internalReaders.add(extra)
  55. reactor.addReader(extra)
  56. reactor._removeAll(reactor._readers, reactor._writers)
  57. self._checkWaker(reactor)
  58. self.assertIn(extra, reactor._internalReaders)
  59. self.assertIn(extra, reactor._readers)
  60. def test_removeAllReturnsRemovedDescriptors(self):
  61. """
  62. L{PosixReactorBase._removeAll} returns a list of removed
  63. L{IReadDescriptor} and L{IWriteDescriptor} objects.
  64. """
  65. reactor = TrivialReactor()
  66. reader = object()
  67. writer = object()
  68. reactor.addReader(reader)
  69. reactor.addWriter(writer)
  70. removed = reactor._removeAll(
  71. reactor._readers, reactor._writers)
  72. self.assertEqual(set(removed), set([reader, writer]))
  73. self.assertNotIn(reader, reactor._readers)
  74. self.assertNotIn(writer, reactor._writers)
  75. class TCPPortTests(TestCase):
  76. """
  77. Tests for L{twisted.internet.tcp.Port}.
  78. """
  79. if not isinstance(reactor, PosixReactorBase):
  80. skip = "Non-posixbase reactor"
  81. def test_connectionLostFailed(self):
  82. """
  83. L{Port.stopListening} returns a L{Deferred} which errbacks if
  84. L{Port.connectionLost} raises an exception.
  85. """
  86. port = Port(12345, ServerFactory())
  87. port.connected = True
  88. port.connectionLost = lambda reason: 1 // 0
  89. return self.assertFailure(port.stopListening(), ZeroDivisionError)
  90. class TimeoutReportReactor(PosixReactorBase):
  91. """
  92. A reactor which is just barely runnable and which cannot monitor any
  93. readers or writers, and which fires a L{Deferred} with the timeout
  94. passed to its C{doIteration} method as soon as that method is invoked.
  95. """
  96. def __init__(self):
  97. PosixReactorBase.__init__(self)
  98. self.iterationTimeout = Deferred()
  99. self.now = 100
  100. def addReader(self, reader):
  101. """
  102. Ignore the reader. This is necessary because the waker will be
  103. added. However, we won't actually monitor it for any events.
  104. """
  105. def removeAll(self):
  106. """
  107. There are no readers or writers, so there is nothing to remove.
  108. This will be called when the reactor stops, though, so it must be
  109. implemented.
  110. """
  111. return []
  112. def seconds(self):
  113. """
  114. Override the real clock with a deterministic one that can be easily
  115. controlled in a unit test.
  116. """
  117. return self.now
  118. def doIteration(self, timeout):
  119. d = self.iterationTimeout
  120. if d is not None:
  121. self.iterationTimeout = None
  122. d.callback(timeout)
  123. class IterationTimeoutTests(TestCase):
  124. """
  125. Tests for the timeout argument L{PosixReactorBase.run} calls
  126. L{PosixReactorBase.doIteration} with in the presence of various delayed
  127. calls.
  128. """
  129. def _checkIterationTimeout(self, reactor):
  130. timeout = []
  131. reactor.iterationTimeout.addCallback(timeout.append)
  132. reactor.iterationTimeout.addCallback(lambda ignored: reactor.stop())
  133. reactor.run()
  134. return timeout[0]
  135. def test_noCalls(self):
  136. """
  137. If there are no delayed calls, C{doIteration} is called with a
  138. timeout of L{None}.
  139. """
  140. reactor = TimeoutReportReactor()
  141. timeout = self._checkIterationTimeout(reactor)
  142. self.assertIsNone(timeout)
  143. def test_delayedCall(self):
  144. """
  145. If there is a delayed call, C{doIteration} is called with a timeout
  146. which is the difference between the current time and the time at
  147. which that call is to run.
  148. """
  149. reactor = TimeoutReportReactor()
  150. reactor.callLater(100, lambda: None)
  151. timeout = self._checkIterationTimeout(reactor)
  152. self.assertEqual(timeout, 100)
  153. def test_timePasses(self):
  154. """
  155. If a delayed call is scheduled and then some time passes, the
  156. timeout passed to C{doIteration} is reduced by the amount of time
  157. which passed.
  158. """
  159. reactor = TimeoutReportReactor()
  160. reactor.callLater(100, lambda: None)
  161. reactor.now += 25
  162. timeout = self._checkIterationTimeout(reactor)
  163. self.assertEqual(timeout, 75)
  164. def test_multipleDelayedCalls(self):
  165. """
  166. If there are several delayed calls, C{doIteration} is called with a
  167. timeout which is the difference between the current time and the
  168. time at which the earlier of the two calls is to run.
  169. """
  170. reactor = TimeoutReportReactor()
  171. reactor.callLater(50, lambda: None)
  172. reactor.callLater(10, lambda: None)
  173. reactor.callLater(100, lambda: None)
  174. timeout = self._checkIterationTimeout(reactor)
  175. self.assertEqual(timeout, 10)
  176. def test_resetDelayedCall(self):
  177. """
  178. If a delayed call is reset, the timeout passed to C{doIteration} is
  179. based on the interval between the time when reset is called and the
  180. new delay of the call.
  181. """
  182. reactor = TimeoutReportReactor()
  183. call = reactor.callLater(50, lambda: None)
  184. reactor.now += 25
  185. call.reset(15)
  186. timeout = self._checkIterationTimeout(reactor)
  187. self.assertEqual(timeout, 15)
  188. def test_delayDelayedCall(self):
  189. """
  190. If a delayed call is re-delayed, the timeout passed to
  191. C{doIteration} is based on the remaining time before the call would
  192. have been made and the additional amount of time passed to the delay
  193. method.
  194. """
  195. reactor = TimeoutReportReactor()
  196. call = reactor.callLater(50, lambda: None)
  197. reactor.now += 10
  198. call.delay(20)
  199. timeout = self._checkIterationTimeout(reactor)
  200. self.assertEqual(timeout, 60)
  201. def test_cancelDelayedCall(self):
  202. """
  203. If the only delayed call is canceled, L{None} is the timeout passed
  204. to C{doIteration}.
  205. """
  206. reactor = TimeoutReportReactor()
  207. call = reactor.callLater(50, lambda: None)
  208. call.cancel()
  209. timeout = self._checkIterationTimeout(reactor)
  210. self.assertIsNone(timeout)
  211. class ConnectedDatagramPortTests(TestCase):
  212. """
  213. Test connected datagram UNIX sockets.
  214. """
  215. if skipSockets is not None:
  216. skip = skipSockets
  217. def test_connectionFailedDoesntCallLoseConnection(self):
  218. """
  219. L{ConnectedDatagramPort} does not call the deprecated C{loseConnection}
  220. in L{ConnectedDatagramPort.connectionFailed}.
  221. """
  222. def loseConnection():
  223. """
  224. Dummy C{loseConnection} method. C{loseConnection} is deprecated and
  225. should not get called.
  226. """
  227. self.fail("loseConnection is deprecated and should not get called.")
  228. port = unix.ConnectedDatagramPort(None, ClientProto())
  229. port.loseConnection = loseConnection
  230. port.connectionFailed("goodbye")
  231. def test_connectionFailedCallsStopListening(self):
  232. """
  233. L{ConnectedDatagramPort} calls L{ConnectedDatagramPort.stopListening}
  234. instead of the deprecated C{loseConnection} in
  235. L{ConnectedDatagramPort.connectionFailed}.
  236. """
  237. self.called = False
  238. def stopListening():
  239. """
  240. Dummy C{stopListening} method.
  241. """
  242. self.called = True
  243. port = unix.ConnectedDatagramPort(None, ClientProto())
  244. port.stopListening = stopListening
  245. port.connectionFailed("goodbye")
  246. self.assertTrue(self.called)