test_udp_internals.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for the internal implementation details of L{twisted.internet.udp}.
  5. """
  6. from __future__ import division, absolute_import
  7. import socket
  8. from twisted.trial import unittest
  9. from twisted.internet.protocol import DatagramProtocol
  10. from twisted.internet import udp
  11. from twisted.python.runtime import platformType
  12. if platformType == 'win32':
  13. from errno import WSAEWOULDBLOCK as EWOULDBLOCK
  14. else:
  15. from errno import EWOULDBLOCK
  16. class StringUDPSocket(object):
  17. """
  18. A fake UDP socket object, which returns a fixed sequence of strings and/or
  19. socket errors. Useful for testing.
  20. @ivar retvals: A C{list} containing either strings or C{socket.error}s.
  21. @ivar connectedAddr: The address the socket is connected to.
  22. """
  23. def __init__(self, retvals):
  24. self.retvals = retvals
  25. self.connectedAddr = None
  26. def connect(self, addr):
  27. self.connectedAddr = addr
  28. def recvfrom(self, size):
  29. """
  30. Return (or raise) the next value from C{self.retvals}.
  31. """
  32. ret = self.retvals.pop(0)
  33. if isinstance(ret, socket.error):
  34. raise ret
  35. return ret, None
  36. class KeepReads(DatagramProtocol):
  37. """
  38. Accumulate reads in a list.
  39. """
  40. def __init__(self):
  41. self.reads = []
  42. def datagramReceived(self, data, addr):
  43. self.reads.append(data)
  44. class ErrorsTests(unittest.SynchronousTestCase):
  45. """
  46. Error handling tests for C{udp.Port}.
  47. """
  48. def test_socketReadNormal(self):
  49. """
  50. Socket reads with some good data followed by a socket error which can
  51. be ignored causes reading to stop, and no log messages to be logged.
  52. """
  53. # Add a fake error to the list of ignorables:
  54. udp._sockErrReadIgnore.append(-7000)
  55. self.addCleanup(udp._sockErrReadIgnore.remove, -7000)
  56. protocol = KeepReads()
  57. port = udp.Port(None, protocol)
  58. # Normal result, no errors
  59. port.socket = StringUDPSocket(
  60. [b"result", b"123", socket.error(-7000), b"456",
  61. socket.error(-7000)])
  62. port.doRead()
  63. # Read stops on error:
  64. self.assertEqual(protocol.reads, [b"result", b"123"])
  65. port.doRead()
  66. self.assertEqual(protocol.reads, [b"result", b"123", b"456"])
  67. def test_readImmediateError(self):
  68. """
  69. If the socket is unconnected, socket reads with an immediate
  70. connection refusal are ignored, and reading stops. The protocol's
  71. C{connectionRefused} method is not called.
  72. """
  73. # Add a fake error to the list of those that count as connection
  74. # refused:
  75. udp._sockErrReadRefuse.append(-6000)
  76. self.addCleanup(udp._sockErrReadRefuse.remove, -6000)
  77. protocol = KeepReads()
  78. # Fail if connectionRefused is called:
  79. protocol.connectionRefused = lambda: 1/0
  80. port = udp.Port(None, protocol)
  81. # Try an immediate "connection refused"
  82. port.socket = StringUDPSocket([b"a", socket.error(-6000), b"b",
  83. socket.error(EWOULDBLOCK)])
  84. port.doRead()
  85. # Read stops on error:
  86. self.assertEqual(protocol.reads, [b"a"])
  87. # Read again:
  88. port.doRead()
  89. self.assertEqual(protocol.reads, [b"a", b"b"])
  90. def test_connectedReadImmediateError(self):
  91. """
  92. If the socket connected, socket reads with an immediate
  93. connection refusal are ignored, and reading stops. The protocol's
  94. C{connectionRefused} method is called.
  95. """
  96. # Add a fake error to the list of those that count as connection
  97. # refused:
  98. udp._sockErrReadRefuse.append(-6000)
  99. self.addCleanup(udp._sockErrReadRefuse.remove, -6000)
  100. protocol = KeepReads()
  101. refused = []
  102. protocol.connectionRefused = lambda: refused.append(True)
  103. port = udp.Port(None, protocol)
  104. port.socket = StringUDPSocket([b"a", socket.error(-6000), b"b",
  105. socket.error(EWOULDBLOCK)])
  106. port.connect("127.0.0.1", 9999)
  107. # Read stops on error:
  108. port.doRead()
  109. self.assertEqual(protocol.reads, [b"a"])
  110. self.assertEqual(refused, [True])
  111. # Read again:
  112. port.doRead()
  113. self.assertEqual(protocol.reads, [b"a", b"b"])
  114. self.assertEqual(refused, [True])
  115. def test_readUnknownError(self):
  116. """
  117. Socket reads with an unknown socket error are raised.
  118. """
  119. protocol = KeepReads()
  120. port = udp.Port(None, protocol)
  121. # Some good data, followed by an unknown error
  122. port.socket = StringUDPSocket([b"good", socket.error(-1337)])
  123. self.assertRaises(socket.error, port.doRead)
  124. self.assertEqual(protocol.reads, [b"good"])