PageRenderTime 50ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/twisted/test/test_unix.py

https://github.com/lvh/twisted
Python | 405 lines | 203 code | 77 blank | 125 comment | 6 complexity | ea9d8990f1749ede6f9c4a5971e68586 MD5 | raw file
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for implementations of L{IReactorUNIX} and L{IReactorUNIXDatagram}.
  5. """
  6. import stat, os, sys, types
  7. import socket
  8. from twisted.internet import interfaces, reactor, protocol, error, address, defer, utils
  9. from twisted.python import lockfile
  10. from twisted.trial import unittest
  11. from twisted.test.test_tcp import MyServerFactory, MyClientFactory
  12. class FailedConnectionClientFactory(protocol.ClientFactory):
  13. def __init__(self, onFail):
  14. self.onFail = onFail
  15. def clientConnectionFailed(self, connector, reason):
  16. self.onFail.errback(reason)
  17. class UnixSocketTestCase(unittest.TestCase):
  18. """
  19. Test unix sockets.
  20. """
  21. def test_peerBind(self):
  22. """
  23. The address passed to the server factory's C{buildProtocol} method and
  24. the address returned by the connected protocol's transport's C{getPeer}
  25. method match the address the client socket is bound to.
  26. """
  27. filename = self.mktemp()
  28. peername = self.mktemp()
  29. serverFactory = MyServerFactory()
  30. connMade = serverFactory.protocolConnectionMade = defer.Deferred()
  31. unixPort = reactor.listenUNIX(filename, serverFactory)
  32. self.addCleanup(unixPort.stopListening)
  33. unixSocket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  34. self.addCleanup(unixSocket.close)
  35. unixSocket.bind(peername)
  36. unixSocket.connect(filename)
  37. def cbConnMade(proto):
  38. expected = address.UNIXAddress(peername)
  39. self.assertEqual(serverFactory.peerAddresses, [expected])
  40. self.assertEqual(proto.transport.getPeer(), expected)
  41. connMade.addCallback(cbConnMade)
  42. return connMade
  43. def test_dumber(self):
  44. """
  45. L{IReactorUNIX.connectUNIX} can be used to connect a client to a server
  46. started with L{IReactorUNIX.listenUNIX}.
  47. """
  48. filename = self.mktemp()
  49. serverFactory = MyServerFactory()
  50. serverConnMade = defer.Deferred()
  51. serverFactory.protocolConnectionMade = serverConnMade
  52. unixPort = reactor.listenUNIX(filename, serverFactory)
  53. self.addCleanup(unixPort.stopListening)
  54. clientFactory = MyClientFactory()
  55. clientConnMade = defer.Deferred()
  56. clientFactory.protocolConnectionMade = clientConnMade
  57. c = reactor.connectUNIX(filename, clientFactory)
  58. d = defer.gatherResults([serverConnMade, clientConnMade])
  59. def allConnected((serverProtocol, clientProtocol)):
  60. # Incidental assertion which may or may not be redundant with some
  61. # other test. This probably deserves its own test method.
  62. self.assertEqual(clientFactory.peerAddresses,
  63. [address.UNIXAddress(filename)])
  64. clientProtocol.transport.loseConnection()
  65. serverProtocol.transport.loseConnection()
  66. d.addCallback(allConnected)
  67. return d
  68. def test_pidFile(self):
  69. """
  70. A lockfile is created and locked when L{IReactorUNIX.listenUNIX} is
  71. called and released when the Deferred returned by the L{IListeningPort}
  72. provider's C{stopListening} method is called back.
  73. """
  74. filename = self.mktemp()
  75. serverFactory = MyServerFactory()
  76. serverConnMade = defer.Deferred()
  77. serverFactory.protocolConnectionMade = serverConnMade
  78. unixPort = reactor.listenUNIX(filename, serverFactory, wantPID=True)
  79. self.assertTrue(lockfile.isLocked(filename + ".lock"))
  80. # XXX This part would test something about the checkPID parameter, but
  81. # it doesn't actually. It should be rewritten to test the several
  82. # different possible behaviors. -exarkun
  83. clientFactory = MyClientFactory()
  84. clientConnMade = defer.Deferred()
  85. clientFactory.protocolConnectionMade = clientConnMade
  86. c = reactor.connectUNIX(filename, clientFactory, checkPID=1)
  87. d = defer.gatherResults([serverConnMade, clientConnMade])
  88. def _portStuff((serverProtocol, clientProto)):
  89. # Incidental assertion which may or may not be redundant with some
  90. # other test. This probably deserves its own test method.
  91. self.assertEqual(clientFactory.peerAddresses,
  92. [address.UNIXAddress(filename)])
  93. clientProto.transport.loseConnection()
  94. serverProtocol.transport.loseConnection()
  95. return unixPort.stopListening()
  96. d.addCallback(_portStuff)
  97. def _check(ignored):
  98. self.failIf(lockfile.isLocked(filename + ".lock"), 'locked')
  99. d.addCallback(_check)
  100. return d
  101. def test_socketLocking(self):
  102. """
  103. L{IReactorUNIX.listenUNIX} raises L{error.CannotListenError} if passed
  104. the name of a file on which a server is already listening.
  105. """
  106. filename = self.mktemp()
  107. serverFactory = MyServerFactory()
  108. unixPort = reactor.listenUNIX(filename, serverFactory, wantPID=True)
  109. self.assertRaises(
  110. error.CannotListenError,
  111. reactor.listenUNIX, filename, serverFactory, wantPID=True)
  112. def stoppedListening(ign):
  113. unixPort = reactor.listenUNIX(filename, serverFactory, wantPID=True)
  114. return unixPort.stopListening()
  115. return unixPort.stopListening().addCallback(stoppedListening)
  116. def _uncleanSocketTest(self, callback):
  117. self.filename = self.mktemp()
  118. source = ("from twisted.internet import protocol, reactor\n"
  119. "reactor.listenUNIX(%r, protocol.ServerFactory(), wantPID=True)\n") % (self.filename,)
  120. env = {'PYTHONPATH': os.pathsep.join(sys.path)}
  121. d = utils.getProcessValue(sys.executable, ("-u", "-c", source), env=env)
  122. d.addCallback(callback)
  123. return d
  124. def test_uncleanServerSocketLocking(self):
  125. """
  126. If passed C{True} for the C{wantPID} parameter, a server can be started
  127. listening with L{IReactorUNIX.listenUNIX} when passed the name of a
  128. file on which a previous server which has not exited cleanly has been
  129. listening using the C{wantPID} option.
  130. """
  131. def ranStupidChild(ign):
  132. # If this next call succeeds, our lock handling is correct.
  133. p = reactor.listenUNIX(self.filename, MyServerFactory(), wantPID=True)
  134. return p.stopListening()
  135. return self._uncleanSocketTest(ranStupidChild)
  136. def test_connectToUncleanServer(self):
  137. """
  138. If passed C{True} for the C{checkPID} parameter, a client connection
  139. attempt made with L{IReactorUNIX.connectUNIX} fails with
  140. L{error.BadFileError}.
  141. """
  142. def ranStupidChild(ign):
  143. d = defer.Deferred()
  144. f = FailedConnectionClientFactory(d)
  145. c = reactor.connectUNIX(self.filename, f, checkPID=True)
  146. return self.assertFailure(d, error.BadFileError)
  147. return self._uncleanSocketTest(ranStupidChild)
  148. def _reprTest(self, serverFactory, factoryName):
  149. """
  150. Test the C{__str__} and C{__repr__} implementations of a UNIX port when
  151. used with the given factory.
  152. """
  153. filename = self.mktemp()
  154. unixPort = reactor.listenUNIX(filename, serverFactory)
  155. connectedString = "<%s on %r>" % (factoryName, filename)
  156. self.assertEqual(repr(unixPort), connectedString)
  157. self.assertEqual(str(unixPort), connectedString)
  158. d = defer.maybeDeferred(unixPort.stopListening)
  159. def stoppedListening(ign):
  160. unconnectedString = "<%s (not listening)>" % (factoryName,)
  161. self.assertEqual(repr(unixPort), unconnectedString)
  162. self.assertEqual(str(unixPort), unconnectedString)
  163. d.addCallback(stoppedListening)
  164. return d
  165. def test_reprWithClassicFactory(self):
  166. """
  167. The two string representations of the L{IListeningPort} returned by
  168. L{IReactorUNIX.listenUNIX} contains the name of the classic factory
  169. class being used and the filename on which the port is listening or
  170. indicates that the port is not listening.
  171. """
  172. class ClassicFactory:
  173. def doStart(self):
  174. pass
  175. def doStop(self):
  176. pass
  177. # Sanity check
  178. self.assertIsInstance(ClassicFactory, types.ClassType)
  179. return self._reprTest(
  180. ClassicFactory(), "twisted.test.test_unix.ClassicFactory")
  181. def test_reprWithNewStyleFactory(self):
  182. """
  183. The two string representations of the L{IListeningPort} returned by
  184. L{IReactorUNIX.listenUNIX} contains the name of the new-style factory
  185. class being used and the filename on which the port is listening or
  186. indicates that the port is not listening.
  187. """
  188. class NewStyleFactory(object):
  189. def doStart(self):
  190. pass
  191. def doStop(self):
  192. pass
  193. # Sanity check
  194. self.assertIsInstance(NewStyleFactory, type)
  195. return self._reprTest(
  196. NewStyleFactory(), "twisted.test.test_unix.NewStyleFactory")
  197. class ClientProto(protocol.ConnectedDatagramProtocol):
  198. started = stopped = False
  199. gotback = None
  200. def __init__(self):
  201. self.deferredStarted = defer.Deferred()
  202. self.deferredGotBack = defer.Deferred()
  203. def stopProtocol(self):
  204. self.stopped = True
  205. def startProtocol(self):
  206. self.started = True
  207. self.deferredStarted.callback(None)
  208. def datagramReceived(self, data):
  209. self.gotback = data
  210. self.deferredGotBack.callback(None)
  211. class ServerProto(protocol.DatagramProtocol):
  212. started = stopped = False
  213. gotwhat = gotfrom = None
  214. def __init__(self):
  215. self.deferredStarted = defer.Deferred()
  216. self.deferredGotWhat = defer.Deferred()
  217. def stopProtocol(self):
  218. self.stopped = True
  219. def startProtocol(self):
  220. self.started = True
  221. self.deferredStarted.callback(None)
  222. def datagramReceived(self, data, addr):
  223. self.gotfrom = addr
  224. self.transport.write("hi back", addr)
  225. self.gotwhat = data
  226. self.deferredGotWhat.callback(None)
  227. class DatagramUnixSocketTestCase(unittest.TestCase):
  228. """
  229. Test datagram UNIX sockets.
  230. """
  231. def test_exchange(self):
  232. """
  233. Test that a datagram can be sent to and received by a server and vice
  234. versa.
  235. """
  236. clientaddr = self.mktemp()
  237. serveraddr = self.mktemp()
  238. sp = ServerProto()
  239. cp = ClientProto()
  240. s = reactor.listenUNIXDatagram(serveraddr, sp)
  241. self.addCleanup(s.stopListening)
  242. c = reactor.connectUNIXDatagram(serveraddr, cp, bindAddress=clientaddr)
  243. self.addCleanup(c.stopListening)
  244. d = defer.gatherResults([sp.deferredStarted, cp.deferredStarted])
  245. def write(ignored):
  246. cp.transport.write("hi")
  247. return defer.gatherResults([sp.deferredGotWhat,
  248. cp.deferredGotBack])
  249. def _cbTestExchange(ignored):
  250. self.assertEqual("hi", sp.gotwhat)
  251. self.assertEqual(clientaddr, sp.gotfrom)
  252. self.assertEqual("hi back", cp.gotback)
  253. d.addCallback(write)
  254. d.addCallback(_cbTestExchange)
  255. return d
  256. def test_cannotListen(self):
  257. """
  258. L{IReactorUNIXDatagram.listenUNIXDatagram} raises
  259. L{error.CannotListenError} if the unix socket specified is already in
  260. use.
  261. """
  262. addr = self.mktemp()
  263. p = ServerProto()
  264. s = reactor.listenUNIXDatagram(addr, p)
  265. self.failUnlessRaises(error.CannotListenError, reactor.listenUNIXDatagram, addr, p)
  266. s.stopListening()
  267. os.unlink(addr)
  268. # test connecting to bound and connected (somewhere else) address
  269. def _reprTest(self, serverProto, protocolName):
  270. """
  271. Test the C{__str__} and C{__repr__} implementations of a UNIX datagram
  272. port when used with the given protocol.
  273. """
  274. filename = self.mktemp()
  275. unixPort = reactor.listenUNIXDatagram(filename, serverProto)
  276. connectedString = "<%s on %r>" % (protocolName, filename)
  277. self.assertEqual(repr(unixPort), connectedString)
  278. self.assertEqual(str(unixPort), connectedString)
  279. stopDeferred = defer.maybeDeferred(unixPort.stopListening)
  280. def stoppedListening(ign):
  281. unconnectedString = "<%s (not listening)>" % (protocolName,)
  282. self.assertEqual(repr(unixPort), unconnectedString)
  283. self.assertEqual(str(unixPort), unconnectedString)
  284. stopDeferred.addCallback(stoppedListening)
  285. return stopDeferred
  286. def test_reprWithClassicProtocol(self):
  287. """
  288. The two string representations of the L{IListeningPort} returned by
  289. L{IReactorUNIXDatagram.listenUNIXDatagram} contains the name of the
  290. classic protocol class being used and the filename on which the port is
  291. listening or indicates that the port is not listening.
  292. """
  293. class ClassicProtocol:
  294. def makeConnection(self, transport):
  295. pass
  296. def doStop(self):
  297. pass
  298. # Sanity check
  299. self.assertIsInstance(ClassicProtocol, types.ClassType)
  300. return self._reprTest(
  301. ClassicProtocol(), "twisted.test.test_unix.ClassicProtocol")
  302. def test_reprWithNewStyleProtocol(self):
  303. """
  304. The two string representations of the L{IListeningPort} returned by
  305. L{IReactorUNIXDatagram.listenUNIXDatagram} contains the name of the
  306. new-style protocol class being used and the filename on which the port
  307. is listening or indicates that the port is not listening.
  308. """
  309. class NewStyleProtocol(object):
  310. def makeConnection(self, transport):
  311. pass
  312. def doStop(self):
  313. pass
  314. # Sanity check
  315. self.assertIsInstance(NewStyleProtocol, type)
  316. return self._reprTest(
  317. NewStyleProtocol(), "twisted.test.test_unix.NewStyleProtocol")
  318. if not interfaces.IReactorUNIX(reactor, None):
  319. UnixSocketTestCase.skip = "This reactor does not support UNIX domain sockets"
  320. if not interfaces.IReactorUNIXDatagram(reactor, None):
  321. DatagramUnixSocketTestCase.skip = "This reactor does not support UNIX datagram sockets"