test_base.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for L{twisted.internet.base}.
  5. """
  6. import socket
  7. try:
  8. from Queue import Queue
  9. except ImportError:
  10. from queue import Queue
  11. from zope.interface import implementer
  12. from twisted.python.threadpool import ThreadPool
  13. from twisted.internet.interfaces import (IReactorTime, IReactorThreads,
  14. IResolverSimple)
  15. from twisted.internet.error import DNSLookupError
  16. from twisted.internet._resolver import FirstOneWins
  17. from twisted.internet.defer import Deferred
  18. from twisted.internet.base import ThreadedResolver, DelayedCall, ReactorBase
  19. from twisted.internet.task import Clock
  20. from twisted.trial.unittest import TestCase
  21. @implementer(IReactorTime, IReactorThreads)
  22. class FakeReactor(object):
  23. """
  24. A fake reactor implementation which just supports enough reactor APIs for
  25. L{ThreadedResolver}.
  26. """
  27. def __init__(self):
  28. self._clock = Clock()
  29. self.callLater = self._clock.callLater
  30. self._threadpool = ThreadPool()
  31. self._threadpool.start()
  32. self.getThreadPool = lambda: self._threadpool
  33. self._threadCalls = Queue()
  34. def callFromThread(self, f, *args, **kwargs):
  35. self._threadCalls.put((f, args, kwargs))
  36. def _runThreadCalls(self):
  37. f, args, kwargs = self._threadCalls.get()
  38. f(*args, **kwargs)
  39. def _stop(self):
  40. self._threadpool.stop()
  41. class ThreadedResolverTests(TestCase):
  42. """
  43. Tests for L{ThreadedResolver}.
  44. """
  45. def test_success(self):
  46. """
  47. L{ThreadedResolver.getHostByName} returns a L{Deferred} which fires
  48. with the value returned by the call to L{socket.gethostbyname} in the
  49. threadpool of the reactor passed to L{ThreadedResolver.__init__}.
  50. """
  51. ip = "10.0.0.17"
  52. name = "foo.bar.example.com"
  53. timeout = 30
  54. reactor = FakeReactor()
  55. self.addCleanup(reactor._stop)
  56. lookedUp = []
  57. resolvedTo = []
  58. def fakeGetHostByName(name):
  59. lookedUp.append(name)
  60. return ip
  61. self.patch(socket, 'gethostbyname', fakeGetHostByName)
  62. resolver = ThreadedResolver(reactor)
  63. d = resolver.getHostByName(name, (timeout,))
  64. d.addCallback(resolvedTo.append)
  65. reactor._runThreadCalls()
  66. self.assertEqual(lookedUp, [name])
  67. self.assertEqual(resolvedTo, [ip])
  68. # Make sure that any timeout-related stuff gets cleaned up.
  69. reactor._clock.advance(timeout + 1)
  70. self.assertEqual(reactor._clock.calls, [])
  71. def test_failure(self):
  72. """
  73. L{ThreadedResolver.getHostByName} returns a L{Deferred} which fires a
  74. L{Failure} if the call to L{socket.gethostbyname} raises an exception.
  75. """
  76. timeout = 30
  77. reactor = FakeReactor()
  78. self.addCleanup(reactor._stop)
  79. def fakeGetHostByName(name):
  80. raise IOError("ENOBUFS (this is a funny joke)")
  81. self.patch(socket, 'gethostbyname', fakeGetHostByName)
  82. failedWith = []
  83. resolver = ThreadedResolver(reactor)
  84. d = resolver.getHostByName("some.name", (timeout,))
  85. self.assertFailure(d, DNSLookupError)
  86. d.addCallback(failedWith.append)
  87. reactor._runThreadCalls()
  88. self.assertEqual(len(failedWith), 1)
  89. # Make sure that any timeout-related stuff gets cleaned up.
  90. reactor._clock.advance(timeout + 1)
  91. self.assertEqual(reactor._clock.calls, [])
  92. def test_timeout(self):
  93. """
  94. If L{socket.gethostbyname} does not complete before the specified
  95. timeout elapsed, the L{Deferred} returned by
  96. L{ThreadedResolver.getHostByName} fails with L{DNSLookupError}.
  97. """
  98. timeout = 10
  99. reactor = FakeReactor()
  100. self.addCleanup(reactor._stop)
  101. result = Queue()
  102. def fakeGetHostByName(name):
  103. raise result.get()
  104. self.patch(socket, 'gethostbyname', fakeGetHostByName)
  105. failedWith = []
  106. resolver = ThreadedResolver(reactor)
  107. d = resolver.getHostByName("some.name", (timeout,))
  108. self.assertFailure(d, DNSLookupError)
  109. d.addCallback(failedWith.append)
  110. reactor._clock.advance(timeout - 1)
  111. self.assertEqual(failedWith, [])
  112. reactor._clock.advance(1)
  113. self.assertEqual(len(failedWith), 1)
  114. # Eventually the socket.gethostbyname does finish - in this case, with
  115. # an exception. Nobody cares, though.
  116. result.put(IOError("The I/O was errorful"))
  117. def test_resolverGivenStr(self):
  118. """
  119. L{ThreadedResolver.getHostByName} is passed L{str}, encoded using IDNA
  120. if required.
  121. """
  122. calls = []
  123. @implementer(IResolverSimple)
  124. class FakeResolver(object):
  125. def getHostByName(self, name, timeouts=()):
  126. calls.append(name)
  127. return Deferred()
  128. class JustEnoughReactor(ReactorBase):
  129. def installWaker(self):
  130. pass
  131. fake = FakeResolver()
  132. reactor = JustEnoughReactor()
  133. reactor.installResolver(fake)
  134. rec = FirstOneWins(Deferred())
  135. reactor.nameResolver.resolveHostName(
  136. rec, u"example.example")
  137. reactor.nameResolver.resolveHostName(
  138. rec, "example.example")
  139. reactor.nameResolver.resolveHostName(
  140. rec, u"v\xe4\xe4ntynyt.example")
  141. reactor.nameResolver.resolveHostName(
  142. rec, u"\u0440\u0444.example")
  143. reactor.nameResolver.resolveHostName(
  144. rec, "xn----7sbb4ac0ad0be6cf.xn--p1ai")
  145. self.assertEqual(len(calls), 5)
  146. self.assertEqual(list(map(type, calls)), [str]*5)
  147. self.assertEqual("example.example", calls[0])
  148. self.assertEqual("example.example", calls[1])
  149. self.assertEqual("xn--vntynyt-5waa.example", calls[2])
  150. self.assertEqual("xn--p1ai.example", calls[3])
  151. self.assertEqual("xn----7sbb4ac0ad0be6cf.xn--p1ai", calls[4])
  152. def nothing():
  153. """
  154. Function used by L{DelayedCallTests.test_str}.
  155. """
  156. class DelayedCallMixin(object):
  157. """
  158. L{DelayedCall}
  159. """
  160. def _getDelayedCallAt(self, time):
  161. """
  162. Get a L{DelayedCall} instance at a given C{time}.
  163. @param time: The absolute time at which the returned L{DelayedCall}
  164. will be scheduled.
  165. """
  166. def noop(call):
  167. pass
  168. return DelayedCall(time, lambda: None, (), {}, noop, noop, None)
  169. def setUp(self):
  170. """
  171. Create two L{DelayedCall} instanced scheduled to run at different
  172. times.
  173. """
  174. self.zero = self._getDelayedCallAt(0)
  175. self.one = self._getDelayedCallAt(1)
  176. def test_str(self):
  177. """
  178. The string representation of a L{DelayedCall} instance, as returned by
  179. L{str}, includes the unsigned id of the instance, as well as its state,
  180. the function to be called, and the function arguments.
  181. """
  182. dc = DelayedCall(12, nothing, (3, ), {"A": 5}, None, None, lambda: 1.5)
  183. self.assertEqual(
  184. str(dc),
  185. "<DelayedCall 0x%x [10.5s] called=0 cancelled=0 nothing(3, A=5)>"
  186. % (id(dc),))
  187. def test_lt(self):
  188. """
  189. For two instances of L{DelayedCall} C{a} and C{b}, C{a < b} is true
  190. if and only if C{a} is scheduled to run before C{b}.
  191. """
  192. zero, one = self.zero, self.one
  193. self.assertTrue(zero < one)
  194. self.assertFalse(one < zero)
  195. self.assertFalse(zero < zero)
  196. self.assertFalse(one < one)
  197. def test_le(self):
  198. """
  199. For two instances of L{DelayedCall} C{a} and C{b}, C{a <= b} is true
  200. if and only if C{a} is scheduled to run before C{b} or at the same
  201. time as C{b}.
  202. """
  203. zero, one = self.zero, self.one
  204. self.assertTrue(zero <= one)
  205. self.assertFalse(one <= zero)
  206. self.assertTrue(zero <= zero)
  207. self.assertTrue(one <= one)
  208. def test_gt(self):
  209. """
  210. For two instances of L{DelayedCall} C{a} and C{b}, C{a > b} is true
  211. if and only if C{a} is scheduled to run after C{b}.
  212. """
  213. zero, one = self.zero, self.one
  214. self.assertTrue(one > zero)
  215. self.assertFalse(zero > one)
  216. self.assertFalse(zero > zero)
  217. self.assertFalse(one > one)
  218. def test_ge(self):
  219. """
  220. For two instances of L{DelayedCall} C{a} and C{b}, C{a > b} is true
  221. if and only if C{a} is scheduled to run after C{b} or at the same
  222. time as C{b}.
  223. """
  224. zero, one = self.zero, self.one
  225. self.assertTrue(one >= zero)
  226. self.assertFalse(zero >= one)
  227. self.assertTrue(zero >= zero)
  228. self.assertTrue(one >= one)
  229. def test_eq(self):
  230. """
  231. A L{DelayedCall} instance is only equal to itself.
  232. """
  233. # Explicitly use == here, instead of assertEqual, to be more
  234. # confident __eq__ is being tested.
  235. self.assertFalse(self.zero == self.one)
  236. self.assertTrue(self.zero == self.zero)
  237. self.assertTrue(self.one == self.one)
  238. def test_ne(self):
  239. """
  240. A L{DelayedCall} instance is not equal to any other object.
  241. """
  242. # Explicitly use != here, instead of assertEqual, to be more
  243. # confident __ne__ is being tested.
  244. self.assertTrue(self.zero != self.one)
  245. self.assertFalse(self.zero != self.zero)
  246. self.assertFalse(self.one != self.one)
  247. class DelayedCallNoDebugTests(DelayedCallMixin, TestCase):
  248. """
  249. L{DelayedCall}
  250. """
  251. def setUp(self):
  252. """
  253. Turn debug off.
  254. """
  255. self.patch(DelayedCall, 'debug', False)
  256. DelayedCallMixin.setUp(self)
  257. def test_str(self):
  258. """
  259. The string representation of a L{DelayedCall} instance, as returned by
  260. L{str}, includes the unsigned id of the instance, as well as its state,
  261. the function to be called, and the function arguments.
  262. """
  263. dc = DelayedCall(12, nothing, (3, ), {"A": 5}, None, None, lambda: 1.5)
  264. expected = (
  265. "<DelayedCall 0x{:x} [10.5s] called=0 cancelled=0 "
  266. "nothing(3, A=5)>".format(id(dc)))
  267. self.assertEqual(str(dc), expected)
  268. class DelayedCallDebugTests(DelayedCallMixin, TestCase):
  269. """
  270. L{DelayedCall}
  271. """
  272. def setUp(self):
  273. """
  274. Turn debug on.
  275. """
  276. self.patch(DelayedCall, 'debug', True)
  277. DelayedCallMixin.setUp(self)
  278. def test_str(self):
  279. """
  280. The string representation of a L{DelayedCall} instance, as returned by
  281. L{str}, includes the unsigned id of the instance, as well as its state,
  282. the function to be called, and the function arguments.
  283. """
  284. dc = DelayedCall(12, nothing, (3, ), {"A": 5}, None, None, lambda: 1.5)
  285. expectedRegexp = (
  286. "<DelayedCall 0x{:x} \\[10.5s\\] called=0 cancelled=0 "
  287. "nothing\\(3, A=5\\)\n\n"
  288. "traceback at creation:".format(id(dc)))
  289. self.assertRegex(
  290. str(dc), expectedRegexp)