test_fdesc.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for L{twisted.internet.fdesc}.
  5. """
  6. import os, sys
  7. import errno
  8. try:
  9. import fcntl
  10. except ImportError:
  11. skip = "not supported on this platform"
  12. else:
  13. from twisted.internet import fdesc
  14. from twisted.python.util import untilConcludes
  15. from twisted.trial import unittest
  16. class NonBlockingTests(unittest.SynchronousTestCase):
  17. """
  18. Tests for L{fdesc.setNonBlocking} and L{fdesc.setBlocking}.
  19. """
  20. def test_setNonBlocking(self):
  21. """
  22. L{fdesc.setNonBlocking} sets a file description to non-blocking.
  23. """
  24. r, w = os.pipe()
  25. self.addCleanup(os.close, r)
  26. self.addCleanup(os.close, w)
  27. self.assertFalse(fcntl.fcntl(r, fcntl.F_GETFL) & os.O_NONBLOCK)
  28. fdesc.setNonBlocking(r)
  29. self.assertTrue(fcntl.fcntl(r, fcntl.F_GETFL) & os.O_NONBLOCK)
  30. def test_setBlocking(self):
  31. """
  32. L{fdesc.setBlocking} sets a file description to blocking.
  33. """
  34. r, w = os.pipe()
  35. self.addCleanup(os.close, r)
  36. self.addCleanup(os.close, w)
  37. fdesc.setNonBlocking(r)
  38. fdesc.setBlocking(r)
  39. self.assertFalse(fcntl.fcntl(r, fcntl.F_GETFL) & os.O_NONBLOCK)
  40. class ReadWriteTests(unittest.SynchronousTestCase):
  41. """
  42. Tests for L{fdesc.readFromFD}, L{fdesc.writeToFD}.
  43. """
  44. def setUp(self):
  45. """
  46. Create a non-blocking pipe that can be used in tests.
  47. """
  48. self.r, self.w = os.pipe()
  49. fdesc.setNonBlocking(self.r)
  50. fdesc.setNonBlocking(self.w)
  51. def tearDown(self):
  52. """
  53. Close pipes.
  54. """
  55. try:
  56. os.close(self.w)
  57. except OSError:
  58. pass
  59. try:
  60. os.close(self.r)
  61. except OSError:
  62. pass
  63. def write(self, d):
  64. """
  65. Write data to the pipe.
  66. """
  67. return fdesc.writeToFD(self.w, d)
  68. def read(self):
  69. """
  70. Read data from the pipe.
  71. """
  72. l = []
  73. res = fdesc.readFromFD(self.r, l.append)
  74. if res is None:
  75. if l:
  76. return l[0]
  77. else:
  78. return b""
  79. else:
  80. return res
  81. def test_writeAndRead(self):
  82. """
  83. Test that the number of bytes L{fdesc.writeToFD} reports as written
  84. with its return value are seen by L{fdesc.readFromFD}.
  85. """
  86. n = self.write(b"hello")
  87. self.assertTrue(n > 0)
  88. s = self.read()
  89. self.assertEqual(len(s), n)
  90. self.assertEqual(b"hello"[:n], s)
  91. def test_writeAndReadLarge(self):
  92. """
  93. Similar to L{test_writeAndRead}, but use a much larger string to verify
  94. the behavior for that case.
  95. """
  96. orig = b"0123456879" * 10000
  97. written = self.write(orig)
  98. self.assertTrue(written > 0)
  99. result = []
  100. resultlength = 0
  101. i = 0
  102. while resultlength < written or i < 50:
  103. result.append(self.read())
  104. resultlength += len(result[-1])
  105. # Increment a counter to be sure we'll exit at some point
  106. i += 1
  107. result = b"".join(result)
  108. self.assertEqual(len(result), written)
  109. self.assertEqual(orig[:written], result)
  110. def test_readFromEmpty(self):
  111. """
  112. Verify that reading from a file descriptor with no data does not raise
  113. an exception and does not result in the callback function being called.
  114. """
  115. l = []
  116. result = fdesc.readFromFD(self.r, l.append)
  117. self.assertEqual(l, [])
  118. self.assertIsNone(result)
  119. def test_readFromCleanClose(self):
  120. """
  121. Test that using L{fdesc.readFromFD} on a cleanly closed file descriptor
  122. returns a connection done indicator.
  123. """
  124. os.close(self.w)
  125. self.assertEqual(self.read(), fdesc.CONNECTION_DONE)
  126. def test_writeToClosed(self):
  127. """
  128. Verify that writing with L{fdesc.writeToFD} when the read end is closed
  129. results in a connection lost indicator.
  130. """
  131. os.close(self.r)
  132. self.assertEqual(self.write(b"s"), fdesc.CONNECTION_LOST)
  133. def test_readFromInvalid(self):
  134. """
  135. Verify that reading with L{fdesc.readFromFD} when the read end is
  136. closed results in a connection lost indicator.
  137. """
  138. os.close(self.r)
  139. self.assertEqual(self.read(), fdesc.CONNECTION_LOST)
  140. def test_writeToInvalid(self):
  141. """
  142. Verify that writing with L{fdesc.writeToFD} when the write end is
  143. closed results in a connection lost indicator.
  144. """
  145. os.close(self.w)
  146. self.assertEqual(self.write(b"s"), fdesc.CONNECTION_LOST)
  147. def test_writeErrors(self):
  148. """
  149. Test error path for L{fdesc.writeTod}.
  150. """
  151. oldOsWrite = os.write
  152. def eagainWrite(fd, data):
  153. err = OSError()
  154. err.errno = errno.EAGAIN
  155. raise err
  156. os.write = eagainWrite
  157. try:
  158. self.assertEqual(self.write(b"s"), 0)
  159. finally:
  160. os.write = oldOsWrite
  161. def eintrWrite(fd, data):
  162. err = OSError()
  163. err.errno = errno.EINTR
  164. raise err
  165. os.write = eintrWrite
  166. try:
  167. self.assertEqual(self.write(b"s"), 0)
  168. finally:
  169. os.write = oldOsWrite
  170. class CloseOnExecTests(unittest.SynchronousTestCase):
  171. """
  172. Tests for L{fdesc._setCloseOnExec} and L{fdesc._unsetCloseOnExec}.
  173. """
  174. program = '''
  175. import os, errno
  176. try:
  177. os.write(%d, b'lul')
  178. except OSError as e:
  179. if e.errno == errno.EBADF:
  180. os._exit(0)
  181. os._exit(5)
  182. except:
  183. os._exit(10)
  184. else:
  185. os._exit(20)
  186. '''
  187. def _execWithFileDescriptor(self, fObj):
  188. pid = os.fork()
  189. if pid == 0:
  190. try:
  191. os.execv(sys.executable, [sys.executable, '-c', self.program % (fObj.fileno(),)])
  192. except:
  193. import traceback
  194. traceback.print_exc()
  195. os._exit(30)
  196. else:
  197. # On Linux wait(2) doesn't seem ever able to fail with EINTR but
  198. # POSIX seems to allow it and on OS X it happens quite a lot.
  199. return untilConcludes(os.waitpid, pid, 0)[1]
  200. def test_setCloseOnExec(self):
  201. """
  202. A file descriptor passed to L{fdesc._setCloseOnExec} is not inherited
  203. by a new process image created with one of the exec family of
  204. functions.
  205. """
  206. with open(self.mktemp(), 'wb') as fObj:
  207. fdesc._setCloseOnExec(fObj.fileno())
  208. status = self._execWithFileDescriptor(fObj)
  209. self.assertTrue(os.WIFEXITED(status))
  210. self.assertEqual(os.WEXITSTATUS(status), 0)
  211. def test_unsetCloseOnExec(self):
  212. """
  213. A file descriptor passed to L{fdesc._unsetCloseOnExec} is inherited by
  214. a new process image created with one of the exec family of functions.
  215. """
  216. with open(self.mktemp(), 'wb') as fObj:
  217. fdesc._setCloseOnExec(fObj.fileno())
  218. fdesc._unsetCloseOnExec(fObj.fileno())
  219. status = self._execWithFileDescriptor(fObj)
  220. self.assertTrue(os.WIFEXITED(status))
  221. self.assertEqual(os.WEXITSTATUS(status), 20)