def test_disorderlyShutdown(self): """ If a L{TLSMemoryBIOProtocol} loses its connection unexpectedly, this is reported to the application. """ clientConnectionLost = Deferred() clientFactory = ClientFactory() clientFactory.protocol = lambda: ConnectionLostNotifyingProtocol(clientConnectionLost) clientContextFactory = HandshakeCallbackContextFactory() wrapperFactory = TLSMemoryBIOFactory(clientContextFactory, True, clientFactory) sslClientProtocol = wrapperFactory.buildProtocol(None) # Client speaks first, so the server can be dumb. serverProtocol = Protocol() connectionDeferred = loopbackAsync(serverProtocol, sslClientProtocol) # Now destroy the connection. serverProtocol.transport.loseConnection() # And when the connection completely dies, check the reason. def cbDisconnected(clientProtocol): clientProtocol.lostConnectionReason.trap(Error) clientConnectionLost.addCallback(cbDisconnected) return clientConnectionLost
def test_disorderlyShutdown(self): """ If a L{TLSMemoryBIOProtocol} loses its connection unexpectedly, this is reported to the application. """ clientConnectionLost = Deferred() clientFactory = ClientFactory() clientFactory.protocol = ( lambda: ConnectionLostNotifyingProtocol(clientConnectionLost)) clientContextFactory = HandshakeCallbackContextFactory() wrapperFactory = TLSMemoryBIOFactory(clientContextFactory, True, clientFactory) sslClientProtocol = wrapperFactory.buildProtocol(None) # Client speaks first, so the server can be dumb. serverProtocol = Protocol() connectionDeferred = loopbackAsync(serverProtocol, sslClientProtocol) # Now destroy the connection. serverProtocol.transport.loseConnection() # And when the connection completely dies, check the reason. def cbDisconnected(clientProtocol): clientProtocol.lostConnectionReason.trap(Error) clientConnectionLost.addCallback(cbDisconnected) return clientConnectionLost
def test_spawnProcessEarlyIsReaped(self): """ If, before the reactor is started with L{IReactorCore.run}, a process is started with L{IReactorProcess.spawnProcess} and terminates, the process is reaped once the reactor is started. """ reactor = self.buildReactor() # Create the process with no shared file descriptors, so that there # are no other events for the reactor to notice and "cheat" with. # We want to be sure it's really dealing with the process exiting, # not some associated event. if self.usePTY: childFDs = None else: childFDs = {} # Arrange to notice the SIGCHLD. signaled = threading.Event() def handler(*args): signaled.set() signal.signal(signal.SIGCHLD, handler) # Start a process - before starting the reactor! ended = Deferred() reactor.spawnProcess(_ShutdownCallbackProcessProtocol(ended), sys.executable, [sys.executable, "-c", ""], usePTY=self.usePTY, childFDs=childFDs) # Wait for the SIGCHLD (which might have been delivered before we got # here, but that's okay because the signal handler was installed above, # before we could have gotten it). signaled.wait(120) if not signaled.isSet(): self.fail("Timed out waiting for child process to exit.") # Capture the processEnded callback. result = [] ended.addCallback(result.append) if result: # The synchronous path through spawnProcess / Process.__init__ / # registerReapProcessHandler was encountered. There's no reason to # start the reactor, because everything is done already. return # Otherwise, though, start the reactor so it can tell us the process # exited. ended.addCallback(lambda ignored: reactor.stop()) self.runReactor(reactor) # Make sure the reactor stopped because the Deferred fired. self.assertTrue(result)
def testFileResponse(self): d = Deferred() d.addCallback(self.fileResponse) self.client.cookies['iCookies'][1234] = (d, None) m = msn.MSNMessage() m.setHeader('Content-Type', 'text/x-msmsgsinvite; charset=UTF-8') m.message += 'Invitation-Command: ACCEPT\r\n' m.message += 'Invitation-Cookie: 1234\r\n\r\n' self.client.checkMessage(m) self.failUnless((self.client.state == 'RESPONSE'), msg='Failed to detect file transfer response')
def testFileResponse(self): d = Deferred() d.addCallback(self.fileResponse) self.client.cookies['iCookies'][1234] = (d, None) m = msn.MSNMessage() m.setHeader('Content-Type', 'text/x-msmsgsinvite; charset=UTF-8') m.message += 'Invitation-Command: ACCEPT\r\n' m.message += 'Invitation-Cookie: 1234\r\n\r\n' self.client.checkMessage(m) self.failUnless((self.client.state == 'RESPONSE'), msg='Failed to detect file transfer response')
def test_spawnProcessEarlyIsReaped(self): """ If, before the reactor is started with L{IReactorCore.run}, a process is started with L{IReactorProcess.spawnProcess} and terminates, the process is reaped once the reactor is started. """ reactor = self.buildReactor() # Create the process with no shared file descriptors, so that there # are no other events for the reactor to notice and "cheat" with. # We want to be sure it's really dealing with the process exiting, # not some associated event. if self.usePTY: childFDs = None else: childFDs = {} # Arrange to notice the SIGCHLD. signaled = threading.Event() def handler(*args): signaled.set() signal.signal(signal.SIGCHLD, handler) # Start a process - before starting the reactor! ended = Deferred() reactor.spawnProcess( _ShutdownCallbackProcessProtocol(ended), sys.executable, [sys.executable, "-c", ""], usePTY=self.usePTY, childFDs=childFDs) # Wait for the SIGCHLD (which might have been delivered before we got # here, but that's okay because the signal handler was installed above, # before we could have gotten it). signaled.wait(120) if not signaled.isSet(): self.fail("Timed out waiting for child process to exit.") # Capture the processEnded callback. result = [] ended.addCallback(result.append) if result: # The synchronous path through spawnProcess / Process.__init__ / # registerReapProcessHandler was encountered. There's no reason to # start the reactor, because everything is done already. return # Otherwise, though, start the reactor so it can tell us the process # exited. ended.addCallback(lambda ignored: reactor.stop()) self.runReactor(reactor) # Make sure the reactor stopped because the Deferred fired. self.assertTrue(result)
def testFileInfo(self): d = Deferred() d.addCallback(self.fileInfo) self.client.cookies['external'][1234] = (d, None) m = msn.MSNMessage() m.setHeader('Content-Type', 'text/x-msmsgsinvite; charset=UTF-8') m.message += 'Invitation-Command: ACCEPT\r\n' m.message += 'Invitation-Cookie: 1234\r\n' m.message += 'IP-Address: 192.168.0.1\r\n' m.message += 'Port: 6891\r\n' m.message += 'AuthCookie: 4321\r\n\r\n' self.client.checkMessage(m) self.failUnless((self.client.state == 'INFO'), msg='Failed to detect file transfer info')
def testFileInfo(self): d = Deferred() d.addCallback(self.fileInfo) self.client.cookies['external'][1234] = (d, None) m = msn.MSNMessage() m.setHeader('Content-Type', 'text/x-msmsgsinvite; charset=UTF-8') m.message += 'Invitation-Command: ACCEPT\r\n' m.message += 'Invitation-Cookie: 1234\r\n' m.message += 'IP-Address: 192.168.0.1\r\n' m.message += 'Port: 6891\r\n' m.message += 'AuthCookie: 4321\r\n\r\n' self.client.checkMessage(m) self.failUnless((self.client.state == 'INFO'), msg='Failed to detect file transfer info')
def test_handshakeFailure(self): """ L{TLSMemoryBIOProtocol} reports errors in the handshake process to the application-level protocol object using its C{connectionLost} method and disconnects the underlying transport. """ clientConnectionLost = Deferred() clientFactory = ClientFactory() clientFactory.protocol = ( lambda: ConnectionLostNotifyingProtocol(clientConnectionLost)) clientContextFactory = HandshakeCallbackContextFactory() wrapperFactory = TLSMemoryBIOFactory(clientContextFactory, True, clientFactory) sslClientProtocol = wrapperFactory.buildProtocol(None) serverConnectionLost = Deferred() serverFactory = ServerFactory() serverFactory.protocol = ( lambda: ConnectionLostNotifyingProtocol(serverConnectionLost)) # This context factory rejects any clients which do not present a # certificate. certificateData = FilePath(certPath).getContent() certificate = PrivateCertificate.loadPEM(certificateData) serverContextFactory = certificate.options(certificate) wrapperFactory = TLSMemoryBIOFactory(serverContextFactory, False, serverFactory) sslServerProtocol = wrapperFactory.buildProtocol(None) connectionDeferred = loopbackAsync(sslServerProtocol, sslClientProtocol) def cbConnectionLost(protocol): # The connection should close on its own in response to the error # induced by the client not supplying the required certificate. # After that, check to make sure the protocol's connectionLost was # called with the right thing. protocol.lostConnectionReason.trap(Error) clientConnectionLost.addCallback(cbConnectionLost) serverConnectionLost.addCallback(cbConnectionLost) # Additionally, the underlying transport should have been told to # go away. return gatherResults( [clientConnectionLost, serverConnectionLost, connectionDeferred])
def test_processEnded(self): """ L{IProcessProtocol.processEnded} is called after the child process exits and L{IProcessProtocol.childConnectionLost} is called for each of its file descriptors. """ ended = Deferred() lost = [] class Ender(ProcessProtocol): def childDataReceived(self, fd, data): msg('childDataReceived(%d, %r)' % (fd, data)) self.transport.loseConnection() def childConnectionLost(self, childFD): msg('childConnectionLost(%d)' % (childFD, )) lost.append(childFD) def processExited(self, reason): msg('processExited(%r)' % (reason, )) def processEnded(self, reason): msg('processEnded(%r)' % (reason, )) ended.callback([reason]) reactor = self.buildReactor() reactor.callWhenRunning(reactor.spawnProcess, Ender(), sys.executable, [ sys.executable, self.keepStdioOpenProgram, "child", self.keepStdioOpenArg ], usePTY=self.usePTY) def cbEnded((failure, )): failure.trap(ProcessDone) self.assertEqual(set(lost), set([0, 1, 2])) ended.addCallback(cbEnded) ended.addErrback(err) ended.addCallback(lambda ign: reactor.stop()) self.runReactor(reactor)
def test_childConnectionLost(self): """ L{IProcessProtocol.childConnectionLost} is called each time a file descriptor associated with a child process is closed. """ connected = Deferred() lost = {0: Deferred(), 1: Deferred(), 2: Deferred()} class Closer(ProcessProtocol): def makeConnection(self, transport): connected.callback(transport) def childConnectionLost(self, childFD): lost[childFD].callback(None) source = ("import os, sys\n" "while 1:\n" " line = sys.stdin.readline().strip()\n" " if not line:\n" " break\n" " os.close(int(line))\n") reactor = self.buildReactor() reactor.callWhenRunning(reactor.spawnProcess, Closer(), sys.executable, [sys.executable, "-c", source], usePTY=self.usePTY) def cbConnected(transport): transport.write('2\n') return lost[2].addCallback(lambda ign: transport) connected.addCallback(cbConnected) def lostSecond(transport): transport.write('1\n') return lost[1].addCallback(lambda ign: transport) connected.addCallback(lostSecond) def lostFirst(transport): transport.write('\n') connected.addCallback(lostFirst) connected.addErrback(err) def cbEnded(ignored): reactor.stop() connected.addCallback(cbEnded) self.runReactor(reactor)
def test_processEnded(self): """ L{IProcessProtocol.processEnded} is called after the child process exits and L{IProcessProtocol.childConnectionLost} is called for each of its file descriptors. """ ended = Deferred() lost = [] class Ender(ProcessProtocol): def childDataReceived(self, fd, data): msg('childDataReceived(%d, %r)' % (fd, data)) self.transport.loseConnection() def childConnectionLost(self, childFD): msg('childConnectionLost(%d)' % (childFD,)) lost.append(childFD) def processExited(self, reason): msg('processExited(%r)' % (reason,)) def processEnded(self, reason): msg('processEnded(%r)' % (reason,)) ended.callback([reason]) reactor = self.buildReactor() reactor.callWhenRunning( reactor.spawnProcess, Ender(), sys.executable, [sys.executable, self.keepStdioOpenProgram, "child", self.keepStdioOpenArg], usePTY=self.usePTY) def cbEnded((failure,)): failure.trap(ProcessDone) self.assertEqual(set(lost), set([0, 1, 2])) ended.addCallback(cbEnded) ended.addErrback(err) ended.addCallback(lambda ign: reactor.stop()) self.runReactor(reactor)
def test_handshakeFailure(self): """ L{TLSMemoryBIOProtocol} reports errors in the handshake process to the application-level protocol object using its C{connectionLost} method and disconnects the underlying transport. """ clientConnectionLost = Deferred() clientFactory = ClientFactory() clientFactory.protocol = lambda: ConnectionLostNotifyingProtocol(clientConnectionLost) clientContextFactory = HandshakeCallbackContextFactory() wrapperFactory = TLSMemoryBIOFactory(clientContextFactory, True, clientFactory) sslClientProtocol = wrapperFactory.buildProtocol(None) serverConnectionLost = Deferred() serverFactory = ServerFactory() serverFactory.protocol = lambda: ConnectionLostNotifyingProtocol(serverConnectionLost) # This context factory rejects any clients which do not present a # certificate. certificateData = FilePath(certPath).getContent() certificate = PrivateCertificate.loadPEM(certificateData) serverContextFactory = certificate.options(certificate) wrapperFactory = TLSMemoryBIOFactory(serverContextFactory, False, serverFactory) sslServerProtocol = wrapperFactory.buildProtocol(None) connectionDeferred = loopbackAsync(sslServerProtocol, sslClientProtocol) def cbConnectionLost(protocol): # The connection should close on its own in response to the error # induced by the client not supplying the required certificate. # After that, check to make sure the protocol's connectionLost was # called with the right thing. protocol.lostConnectionReason.trap(Error) clientConnectionLost.addCallback(cbConnectionLost) serverConnectionLost.addCallback(cbConnectionLost) # Additionally, the underlying transport should have been told to # go away. return gatherResults([clientConnectionLost, serverConnectionLost, connectionDeferred])
def test_childConnectionLost(self): """ L{IProcessProtocol.childConnectionLost} is called each time a file descriptor associated with a child process is closed. """ connected = Deferred() lost = {0: Deferred(), 1: Deferred(), 2: Deferred()} class Closer(ProcessProtocol): def makeConnection(self, transport): connected.callback(transport) def childConnectionLost(self, childFD): lost[childFD].callback(None) source = ( "import os, sys\n" "while 1:\n" " line = sys.stdin.readline().strip()\n" " if not line:\n" " break\n" " os.close(int(line))\n") reactor = self.buildReactor() reactor.callWhenRunning( reactor.spawnProcess, Closer(), sys.executable, [sys.executable, "-c", source], usePTY=self.usePTY) def cbConnected(transport): transport.write('2\n') return lost[2].addCallback(lambda ign: transport) connected.addCallback(cbConnected) def lostSecond(transport): transport.write('1\n') return lost[1].addCallback(lambda ign: transport) connected.addCallback(lostSecond) def lostFirst(transport): transport.write('\n') connected.addCallback(lostFirst) connected.addErrback(err) def cbEnded(ignored): reactor.stop() connected.addCallback(cbEnded) self.runReactor(reactor)
def test_processExited(self): """ L{IProcessProtocol.processExited} is called when the child process exits, even if file descriptors associated with the child are still open. """ exited = Deferred() allLost = Deferred() lost = [] class Waiter(ProcessProtocol): def childDataReceived(self, fd, data): msg('childDataReceived(%d, %r)' % (fd, data)) def childConnectionLost(self, childFD): msg('childConnectionLost(%d)' % (childFD, )) lost.append(childFD) if len(lost) == 3: allLost.callback(None) def processExited(self, reason): msg('processExited(%r)' % (reason, )) # See test_processExitedWithSignal exited.callback([reason]) self.transport.loseConnection() reactor = self.buildReactor() reactor.callWhenRunning(reactor.spawnProcess, Waiter(), sys.executable, [ sys.executable, self.keepStdioOpenProgram, "child", self.keepStdioOpenArg ], usePTY=self.usePTY) def cbExited((failure, )): failure.trap(ProcessDone) msg('cbExited; lost = %s' % (lost, )) self.assertEqual(lost, []) return allLost exited.addCallback(cbExited) def cbAllLost(ignored): self.assertEqual(set(lost), set([0, 1, 2])) exited.addCallback(cbAllLost) exited.addErrback(err) exited.addCallback(lambda ign: reactor.stop()) self.runReactor(reactor)
def test_processExited(self): """ L{IProcessProtocol.processExited} is called when the child process exits, even if file descriptors associated with the child are still open. """ exited = Deferred() allLost = Deferred() lost = [] class Waiter(ProcessProtocol): def childDataReceived(self, fd, data): msg('childDataReceived(%d, %r)' % (fd, data)) def childConnectionLost(self, childFD): msg('childConnectionLost(%d)' % (childFD,)) lost.append(childFD) if len(lost) == 3: allLost.callback(None) def processExited(self, reason): msg('processExited(%r)' % (reason,)) # See test_processExitedWithSignal exited.callback([reason]) self.transport.loseConnection() reactor = self.buildReactor() reactor.callWhenRunning( reactor.spawnProcess, Waiter(), sys.executable, [sys.executable, self.keepStdioOpenProgram, "child", self.keepStdioOpenArg], usePTY=self.usePTY) def cbExited((failure,)): failure.trap(ProcessDone) msg('cbExited; lost = %s' % (lost,)) self.assertEqual(lost, []) return allLost exited.addCallback(cbExited) def cbAllLost(ignored): self.assertEqual(set(lost), set([0, 1, 2])) exited.addCallback(cbAllLost) exited.addErrback(err) exited.addCallback(lambda ign: reactor.stop()) self.runReactor(reactor)
class PassportTests(unittest.TestCase): def setUp(self): self.result = [] self.deferred = Deferred() self.deferred.addCallback(lambda r: self.result.append(r)) self.deferred.addErrback(printError) def test_nexus(self): """ When L{msn.PassportNexus} receives enough information to identify the address of the login server, it fires the L{Deferred} passed to its initializer with that address. """ protocol = msn.PassportNexus(self.deferred, 'https://foobar.com/somepage.quux') headers = { 'Content-Length' : '0', 'Content-Type' : 'text/html', 'PassportURLs' : 'DARealm=Passport.Net,DALogin=login.myserver.com/,DAReg=reg.myserver.com' } transport = StringTransport() protocol.makeConnection(transport) protocol.dataReceived('HTTP/1.0 200 OK\r\n') for (h, v) in headers.items(): protocol.dataReceived('%s: %s\r\n' % (h,v)) protocol.dataReceived('\r\n') self.assertEquals(self.result[0], "https://login.myserver.com/") def _doLoginTest(self, response, headers): protocol = msn.PassportLogin(self.deferred,'*****@*****.**','testpass','https://foo.com/', 'a') protocol.makeConnection(StringTransport()) protocol.dataReceived(response) for (h,v) in headers.items(): protocol.dataReceived('%s: %s\r\n' % (h,v)) protocol.dataReceived('\r\n') def testPassportLoginSuccess(self): headers = { 'Content-Length' : '0', 'Content-Type' : 'text/html', 'Authentication-Info' : "Passport1.4 da-status=success,tname=MSPAuth," + "tname=MSPProf,tname=MSPSec,from-PP='somekey'," + "ru=http://messenger.msn.com" } self._doLoginTest('HTTP/1.1 200 OK\r\n', headers) self.failUnless(self.result[0] == (msn.LOGIN_SUCCESS, 'somekey')) def testPassportLoginFailure(self): headers = { 'Content-Type' : 'text/html', 'WWW-Authenticate' : 'Passport1.4 da-status=failed,' + 'srealm=Passport.NET,ts=-3,prompt,cburl=http://host.com,' + 'cbtxt=the%20error%20message' } self._doLoginTest('HTTP/1.1 401 Unauthorized\r\n', headers) self.failUnless(self.result[0] == (msn.LOGIN_FAILURE, 'the error message')) def testPassportLoginRedirect(self): headers = { 'Content-Type' : 'text/html', 'Authentication-Info' : 'Passport1.4 da-status=redir', 'Location' : 'https://newlogin.host.com/' } self._doLoginTest('HTTP/1.1 302 Found\r\n', headers) self.failUnless(self.result[0] == (msn.LOGIN_REDIRECT, 'https://newlogin.host.com/', 'a'))
def trigger(): events.append('trigger') d = Deferred() d.addCallback(callback) reactor.callLater(0, d.callback, None) return d
class PassportTests(unittest.TestCase): def setUp(self): self.result = [] self.deferred = Deferred() self.deferred.addCallback(lambda r: self.result.append(r)) self.deferred.addErrback(printError) def test_nexus(self): """ When L{msn.PassportNexus} receives enough information to identify the address of the login server, it fires the L{Deferred} passed to its initializer with that address. """ protocol = msn.PassportNexus(self.deferred, 'https://foobar.com/somepage.quux') headers = { 'Content-Length': '0', 'Content-Type': 'text/html', 'PassportURLs': 'DARealm=Passport.Net,DALogin=login.myserver.com/,DAReg=reg.myserver.com' } transport = StringTransport() protocol.makeConnection(transport) protocol.dataReceived('HTTP/1.0 200 OK\r\n') for (h, v) in headers.items(): protocol.dataReceived('%s: %s\r\n' % (h, v)) protocol.dataReceived('\r\n') self.assertEquals(self.result[0], "https://login.myserver.com/") def _doLoginTest(self, response, headers): protocol = msn.PassportLogin(self.deferred, '*****@*****.**', 'testpass', 'https://foo.com/', 'a') protocol.makeConnection(StringTransport()) protocol.dataReceived(response) for (h, v) in headers.items(): protocol.dataReceived('%s: %s\r\n' % (h, v)) protocol.dataReceived('\r\n') def testPassportLoginSuccess(self): headers = { 'Content-Length': '0', 'Content-Type': 'text/html', 'Authentication-Info': "Passport1.4 da-status=success,tname=MSPAuth," + "tname=MSPProf,tname=MSPSec,from-PP='somekey'," + "ru=http://messenger.msn.com" } self._doLoginTest('HTTP/1.1 200 OK\r\n', headers) self.failUnless(self.result[0] == (msn.LOGIN_SUCCESS, 'somekey')) def testPassportLoginFailure(self): headers = { 'Content-Type': 'text/html', 'WWW-Authenticate': 'Passport1.4 da-status=failed,' + 'srealm=Passport.NET,ts=-3,prompt,cburl=http://host.com,' + 'cbtxt=the%20error%20message' } self._doLoginTest('HTTP/1.1 401 Unauthorized\r\n', headers) self.failUnless(self.result[0] == (msn.LOGIN_FAILURE, 'the error message')) def testPassportLoginRedirect(self): headers = { 'Content-Type': 'text/html', 'Authentication-Info': 'Passport1.4 da-status=redir', 'Location': 'https://newlogin.host.com/' } self._doLoginTest('HTTP/1.1 302 Found\r\n', headers) self.failUnless(self.result[0] == (msn.LOGIN_REDIRECT, 'https://newlogin.host.com/', 'a'))
def test_processExitedWithSignal(self): """ The C{reason} argument passed to L{IProcessProtocol.processExited} is a L{ProcessTerminated} instance if the child process exits with a signal. """ sigName = 'TERM' sigNum = getattr(signal, 'SIG' + sigName) exited = Deferred() source = ( "import sys\n" # Talk so the parent process knows the process is running. This is # necessary because ProcessProtocol.makeConnection may be called # before this process is exec'd. It would be unfortunate if we # SIGTERM'd the Twisted process while it was on its way to doing # the exec. "sys.stdout.write('x')\n" "sys.stdout.flush()\n" "sys.stdin.read()\n") class Exiter(ProcessProtocol): def childDataReceived(self, fd, data): msg('childDataReceived(%d, %r)' % (fd, data)) self.transport.signalProcess(sigName) def childConnectionLost(self, fd): msg('childConnectionLost(%d)' % (fd,)) def processExited(self, reason): msg('processExited(%r)' % (reason,)) # Protect the Deferred from the failure so that it follows # the callback chain. This doesn't use the errback chain # because it wants to make sure reason is a Failure. An # Exception would also make an errback-based test pass, and # that would be wrong. exited.callback([reason]) def processEnded(self, reason): msg('processEnded(%r)' % (reason,)) reactor = self.buildReactor() reactor.callWhenRunning( reactor.spawnProcess, Exiter(), sys.executable, [sys.executable, "-c", source], usePTY=self.usePTY) def cbExited((failure,)): # Trapping implicitly verifies that it's a Failure (rather than # an exception) and explicitly makes sure it's the right type. failure.trap(ProcessTerminated) err = failure.value if platform.isWindows(): # Windows can't really /have/ signals, so it certainly can't # report them as the reason for termination. Maybe there's # something better we could be doing here, anyway? Hard to # say. Anyway, this inconsistency between different platforms # is extremely unfortunate and I would remove it if I # could. -exarkun self.assertIdentical(err.signal, None) self.assertEqual(err.exitCode, 1) else: self.assertEqual(err.signal, sigNum) self.assertIdentical(err.exitCode, None) exited.addCallback(cbExited) exited.addErrback(err) exited.addCallback(lambda ign: reactor.stop()) self.runReactor(reactor)
def test_processExitedWithSignal(self): """ The C{reason} argument passed to L{IProcessProtocol.processExited} is a L{ProcessTerminated} instance if the child process exits with a signal. """ sigName = 'TERM' sigNum = getattr(signal, 'SIG' + sigName) exited = Deferred() source = ( "import sys\n" # Talk so the parent process knows the process is running. This is # necessary because ProcessProtocol.makeConnection may be called # before this process is exec'd. It would be unfortunate if we # SIGTERM'd the Twisted process while it was on its way to doing # the exec. "sys.stdout.write('x')\n" "sys.stdout.flush()\n" "sys.stdin.read()\n") class Exiter(ProcessProtocol): def childDataReceived(self, fd, data): msg('childDataReceived(%d, %r)' % (fd, data)) self.transport.signalProcess(sigName) def childConnectionLost(self, fd): msg('childConnectionLost(%d)' % (fd, )) def processExited(self, reason): msg('processExited(%r)' % (reason, )) # Protect the Deferred from the failure so that it follows # the callback chain. This doesn't use the errback chain # because it wants to make sure reason is a Failure. An # Exception would also make an errback-based test pass, and # that would be wrong. exited.callback([reason]) def processEnded(self, reason): msg('processEnded(%r)' % (reason, )) reactor = self.buildReactor() reactor.callWhenRunning(reactor.spawnProcess, Exiter(), sys.executable, [sys.executable, "-c", source], usePTY=self.usePTY) def cbExited((failure, )): # Trapping implicitly verifies that it's a Failure (rather than # an exception) and explicitly makes sure it's the right type. failure.trap(ProcessTerminated) err = failure.value if platform.isWindows(): # Windows can't really /have/ signals, so it certainly can't # report them as the reason for termination. Maybe there's # something better we could be doing here, anyway? Hard to # say. Anyway, this inconsistency between different platforms # is extremely unfortunate and I would remove it if I # could. -exarkun self.assertIdentical(err.signal, None) self.assertEqual(err.exitCode, 1) else: self.assertEqual(err.signal, sigNum) self.assertIdentical(err.exitCode, None) exited.addCallback(cbExited) exited.addErrback(err) exited.addCallback(lambda ign: reactor.stop()) self.runReactor(reactor)
def trigger(): events.append('trigger') d = Deferred() d.addCallback(callback) reactor.callLater(0, d.callback, None) return d