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