self.patch(FileTransferForTestAvatar, "_getAttrs", _getAttrs) self.server.factory.expectedLoseConnection = True cmds = ('-o', 'IdentityFile=dsa_test', '-o', 'UserKnownHostsFile=kh_test', '-o', 'HostKeyAlgorithms=ssh-rsa', '-o', 'Port=%i' % (port,), '-b', fn, '[email protected]') d = getProcessOutputAndValue("sftp", cmds) def check(result): self.assertEqual(result[2], 0) for i in ['testDirectory', 'testRemoveFile', 'testRenameFile', 'testfile1']: self.assertIn(i, result[0]) return d.addCallback(check) if unix is None or Crypto is None or pyasn1 is None or interfaces.IReactorProcess(reactor, None) is None: if _reason is None: _reason = "don't run w/o spawnProcess or PyCrypto or pyasn1" OurServerCmdLineClientTests.skip = _reason OurServerBatchFileTests.skip = _reason OurServerSftpClientTests.skip = _reason StdioClientTests.skip = _reason SSHSessionTests.skip = _reason else: from twisted.python.procutils import which if not which('sftp'): OurServerSftpClientTests.skip = "no sftp command-line client available"
if _PY3: expected_output = (b'done 127.0.0.1' + os.linesep.encode("ascii")) else: expected_output = b'done 127.0.0.1\n' if output != expected_output: self.fail(( "The child process failed to produce the desired results:\n" " Reason for termination was: %r\n" " Output stream was: %r\n" " Error stream was: %r\n") % (reason.getErrorMessage(), output, b''.join(error))) helperDeferred.addCallback(cbFinished) return helperDeferred if not interfaces.IReactorProcess(reactor, None): ResolveTests.skip = ( "cannot run test: reactor doesn't support IReactorProcess") class CallFromThreadTests(unittest.TestCase): """ Task scheduling from threads tests. """ if interfaces.IReactorThreads(reactor, None) is None: skip = "Nothing to test without thread support" def setUp(self): self.counter = 0 self.deferred = Deferred()
self.server.factory.expectedLoseConnection = 1 d = getProcessOutputAndValue(sys.executable, cmds, env=env) def _cleanup(res): os.remove(fn) return res d.addCallback(lambda res: res[0]) d.addBoth(_cleanup) return d def testBatchFile(self): """Test that the client works even over a UNIX connection. """ cmds = """pwd exit """ d = self._getBatchOutput(cmds) d.addCallback( lambda res : self.failIf(res.find(self.testDir) == -1, "%s not in %r" % (self.testDir, res))) return d if not unix or not Crypto or not interfaces.IReactorProcess(reactor, None): TestOurServerCmdLineClient.skip = "don't run w/o spawnprocess or PyCrypto" TestOurServerBatchFile.skip = "don't run w/o spawnProcess or PyCrypto" TestOurServerUnixClient.skip = "don't run w/o spawnProcess or PyCrypto"
class ProcessUtilsTests(unittest.TestCase): """ Test running a process using L{getProcessOutput}, L{getProcessValue}, and L{getProcessOutputAndValue}. """ if interfaces.IReactorProcess(reactor, None) is None: skip = "reactor doesn't implement IReactorProcess" output = None value = None exe = sys.executable def makeSourceFile(self, sourceLines): """ Write the given list of lines to a text file and return the absolute path to it. """ script = self.mktemp() scriptFile = file(script, 'wt') scriptFile.write(os.linesep.join(sourceLines) + os.linesep) scriptFile.close() return os.path.abspath(script) def test_output(self): """ L{getProcessOutput} returns a L{Deferred} which fires with the complete output of the process it runs after that process exits. """ scriptFile = self.makeSourceFile([ "import sys", "for s in 'hello world\\n':", " sys.stdout.write(s)", " sys.stdout.flush()" ]) d = utils.getProcessOutput(self.exe, ['-u', scriptFile]) return d.addCallback(self.assertEqual, "hello world\n") def test_outputWithErrorIgnored(self): """ The L{Deferred} returned by L{getProcessOutput} is fired with an L{IOError} L{Failure} if the child process writes to stderr. """ # make sure stderr raises an error normally scriptFile = self.makeSourceFile( ['import sys', 'sys.stderr.write("hello world\\n")']) d = utils.getProcessOutput(self.exe, ['-u', scriptFile]) d = self.assertFailure(d, IOError) def cbFailed(err): return self.assertFailure(err.processEnded, error.ProcessDone) d.addCallback(cbFailed) return d def test_outputWithErrorCollected(self): """ If a C{True} value is supplied for the C{errortoo} parameter to L{getProcessOutput}, the returned L{Deferred} fires with the child's stderr output as well as its stdout output. """ scriptFile = self.makeSourceFile([ 'import sys', # Write the same value to both because ordering isn't guaranteed so # this simplifies the test. 'sys.stdout.write("foo")', 'sys.stdout.flush()', 'sys.stderr.write("foo")', 'sys.stderr.flush()' ]) d = utils.getProcessOutput(self.exe, ['-u', scriptFile], errortoo=True) return d.addCallback(self.assertEqual, "foofoo") def test_value(self): """ The L{Deferred} returned by L{getProcessValue} is fired with the exit status of the child process. """ scriptFile = self.makeSourceFile(["raise SystemExit(1)"]) d = utils.getProcessValue(self.exe, ['-u', scriptFile]) return d.addCallback(self.assertEqual, 1) def test_outputAndValue(self): """ The L{Deferred} returned by L{getProcessOutputAndValue} fires with a three-tuple, the elements of which give the data written to the child's stdout, the data written to the child's stderr, and the exit status of the child. """ exe = sys.executable scriptFile = self.makeSourceFile([ "import sys", "sys.stdout.write('hello world!\\n')", "sys.stderr.write('goodbye world!\\n')", "sys.exit(1)" ]) def gotOutputAndValue((out, err, code)): self.assertEqual(out, "hello world!\n") self.assertEqual(err, "goodbye world!" + os.linesep) self.assertEqual(code, 1) d = utils.getProcessOutputAndValue(self.exe, ["-u", scriptFile]) return d.addCallback(gotOutputAndValue) def test_outputSignal(self): """ If the child process exits because of a signal, the L{Deferred} returned by L{getProcessOutputAndValue} fires a L{Failure} of a tuple containing the the child's stdout, stderr, and the signal which caused it to exit. """ # Use SIGKILL here because it's guaranteed to be delivered. Using # SIGHUP might not work in, e.g., a buildbot slave run under the # 'nohup' command. scriptFile = self.makeSourceFile([ "import sys, os, signal", "sys.stdout.write('stdout bytes\\n')", "sys.stderr.write('stderr bytes\\n')", "sys.stdout.flush()", "sys.stderr.flush()", "os.kill(os.getpid(), signal.SIGKILL)" ]) def gotOutputAndValue((out, err, sig)): self.assertEqual(out, "stdout bytes\n") self.assertEqual(err, "stderr bytes\n") self.assertEqual(sig, signal.SIGKILL) d = utils.getProcessOutputAndValue(self.exe, ['-u', scriptFile]) d = self.assertFailure(d, tuple) return d.addCallback(gotOutputAndValue) if platform.isWindows(): test_outputSignal.skip = "Windows doesn't have real signals." def _pathTest(self, utilFunc, check): dir = os.path.abspath(self.mktemp()) os.makedirs(dir) scriptFile = self.makeSourceFile( ["import os, sys", "sys.stdout.write(os.getcwd())"]) d = utilFunc(self.exe, ['-u', scriptFile], path=dir) d.addCallback(check, dir) return d def test_getProcessOutputPath(self): """ L{getProcessOutput} runs the given command with the working directory given by the C{path} parameter. """ return self._pathTest(utils.getProcessOutput, self.assertEqual) def test_getProcessValuePath(self): """ L{getProcessValue} runs the given command with the working directory given by the C{path} parameter. """ def check(result, ignored): self.assertEqual(result, 0) return self._pathTest(utils.getProcessValue, check) def test_getProcessOutputAndValuePath(self): """ L{getProcessOutputAndValue} runs the given command with the working directory given by the C{path} parameter. """ def check((out, err, status), dir): self.assertEqual(out, dir) self.assertEqual(status, 0) return self._pathTest(utils.getProcessOutputAndValue, check)
(out, err)) if err: log.msg("Unexpected output on standard error: %s" % (err, )) self.assertFalse( out, "Expected no output, instead received:\n%s" % (out, )) def programTimeout(err): err.trap(error.TimeoutError) proto.signalProcess('KILL') return err env = os.environ.copy() env['PYTHONPATH'] = os.pathsep.join(sys.path) d = defer.Deferred().addCallbacks(programFinished, programTimeout) proto = ThreadStartupProcessProtocol(d) reactor.spawnProcess(proto, sys.executable, ('python', progname), env) return d if interfaces.IReactorThreads(reactor, None) is None: for cls in (ReactorThreadsTests, DeferredResultTests, StartupBehaviorTests): cls.skip = "No thread support, nothing to test here." else: import threading if interfaces.IReactorProcess(reactor, None) is None: for cls in (StartupBehaviorTests, ): cls.skip = "No process support, cannot run subprocess thread tests."
class ProcessUtilsTests(unittest.TestCase): """ Test running a process using L{getProcessOutput}, L{getProcessValue}, and L{getProcessOutputAndValue}. """ if interfaces.IReactorProcess(reactor, None) is None: skip = "reactor doesn't implement IReactorProcess" output = None value = None exe = sys.executable def makeSourceFile(self, sourceLines): """ Write the given list of lines to a text file and return the absolute path to it. """ script = self.mktemp() with open(script, 'wt') as scriptFile: scriptFile.write(os.linesep.join(sourceLines) + os.linesep) return os.path.abspath(script) def test_output(self): """ L{getProcessOutput} returns a L{Deferred} which fires with the complete output of the process it runs after that process exits. """ scriptFile = self.makeSourceFile([ "import sys", "for s in b'hello world\\n':", " if hasattr(sys.stdout, 'buffer'):", " # Python 3", " s = bytes([s])", " sys.stdout.buffer.write(s)", " else:", " # Python 2", " sys.stdout.write(s)", " sys.stdout.flush()" ]) d = utils.getProcessOutput(self.exe, ['-u', scriptFile]) return d.addCallback(self.assertEqual, b"hello world\n") def test_outputWithErrorIgnored(self): """ The L{Deferred} returned by L{getProcessOutput} is fired with an L{IOError} L{Failure} if the child process writes to stderr. """ # make sure stderr raises an error normally scriptFile = self.makeSourceFile( ['import sys', 'sys.stderr.write("hello world\\n")']) d = utils.getProcessOutput(self.exe, ['-u', scriptFile]) d = self.assertFailure(d, IOError) def cbFailed(err): return self.assertFailure(err.processEnded, error.ProcessDone) d.addCallback(cbFailed) return d def test_outputWithErrorCollected(self): """ If a C{True} value is supplied for the C{errortoo} parameter to L{getProcessOutput}, the returned L{Deferred} fires with the child's stderr output as well as its stdout output. """ scriptFile = self.makeSourceFile([ 'import sys', # Write the same value to both because ordering isn't guaranteed so # this simplifies the test. 'sys.stdout.write("foo")', 'sys.stdout.flush()', 'sys.stderr.write("foo")', 'sys.stderr.flush()' ]) d = utils.getProcessOutput(self.exe, ['-u', scriptFile], errortoo=True) return d.addCallback(self.assertEqual, b"foofoo") def test_value(self): """ The L{Deferred} returned by L{getProcessValue} is fired with the exit status of the child process. """ scriptFile = self.makeSourceFile(["raise SystemExit(1)"]) d = utils.getProcessValue(self.exe, ['-u', scriptFile]) return d.addCallback(self.assertEqual, 1) def test_outputAndValue(self): """ The L{Deferred} returned by L{getProcessOutputAndValue} fires with a three-tuple, the elements of which give the data written to the child's stdout, the data written to the child's stderr, and the exit status of the child. """ scriptFile = self.makeSourceFile([ "import sys", "if hasattr(sys.stdout, 'buffer'):", " # Python 3", " sys.stdout.buffer.write(b'hello world!\\n')", " sys.stderr.buffer.write(b'goodbye world!\\n')", "else:", " # Python 2", " sys.stdout.write(b'hello world!\\n')", " sys.stderr.write(b'goodbye world!\\n')", "sys.exit(1)" ]) def gotOutputAndValue(out_err_code): out, err, code = out_err_code self.assertEqual(out, b"hello world!\n") if _PY3: self.assertEqual(err, b"goodbye world!\n") else: self.assertEqual(err, b"goodbye world!" + os.linesep) self.assertEqual(code, 1) d = utils.getProcessOutputAndValue(self.exe, ["-u", scriptFile]) return d.addCallback(gotOutputAndValue) def test_outputSignal(self): """ If the child process exits because of a signal, the L{Deferred} returned by L{getProcessOutputAndValue} fires a L{Failure} of a tuple containing the child's stdout, stderr, and the signal which caused it to exit. """ # Use SIGKILL here because it's guaranteed to be delivered. Using # SIGHUP might not work in, e.g., a buildbot subordinate run under the # 'nohup' command. scriptFile = self.makeSourceFile([ "import sys, os, signal", "sys.stdout.write('stdout bytes\\n')", "sys.stderr.write('stderr bytes\\n')", "sys.stdout.flush()", "sys.stderr.flush()", "os.kill(os.getpid(), signal.SIGKILL)" ]) def gotOutputAndValue(out_err_sig): out, err, sig = out_err_sig self.assertEqual(out, b"stdout bytes\n") self.assertEqual(err, b"stderr bytes\n") self.assertEqual(sig, signal.SIGKILL) d = utils.getProcessOutputAndValue(self.exe, ['-u', scriptFile]) d = self.assertFailure(d, tuple) return d.addCallback(gotOutputAndValue) if platform.isWindows(): test_outputSignal.skip = "Windows doesn't have real signals." def _pathTest(self, utilFunc, check): dir = os.path.abspath(self.mktemp()) os.makedirs(dir) scriptFile = self.makeSourceFile( ["import os, sys", "sys.stdout.write(os.getcwd())"]) d = utilFunc(self.exe, ['-u', scriptFile], path=dir) d.addCallback(check, dir.encode(sys.getfilesystemencoding())) return d def test_getProcessOutputPath(self): """ L{getProcessOutput} runs the given command with the working directory given by the C{path} parameter. """ return self._pathTest(utils.getProcessOutput, self.assertEqual) def test_getProcessValuePath(self): """ L{getProcessValue} runs the given command with the working directory given by the C{path} parameter. """ def check(result, ignored): self.assertEqual(result, 0) return self._pathTest(utils.getProcessValue, check) def test_getProcessOutputAndValuePath(self): """ L{getProcessOutputAndValue} runs the given command with the working directory given by the C{path} parameter. """ def check(out_err_status, dir): out, err, status = out_err_status self.assertEqual(out, dir) self.assertEqual(status, 0) return self._pathTest(utils.getProcessOutputAndValue, check) def _defaultPathTest(self, utilFunc, check): # Make another directory to mess around with. dir = os.path.abspath(self.mktemp()) os.makedirs(dir) scriptFile = self.makeSourceFile([ "import os, sys, stat", # Fix the permissions so we can report the working directory. # On macOS (and maybe elsewhere), os.getcwd() fails with EACCES # if +x is missing from the working directory. "os.chmod(%r, stat.S_IXUSR)" % (dir, ), "sys.stdout.write(os.getcwd())" ]) # Switch to it, but make sure we switch back self.addCleanup(os.chdir, os.getcwd()) os.chdir(dir) # Get rid of all its permissions, but make sure they get cleaned up # later, because otherwise it might be hard to delete the trial # temporary directory. self.addCleanup(os.chmod, dir, stat.S_IMODE(os.stat('.').st_mode)) os.chmod(dir, 0) # Pass in -S so that if run using the coverage .pth trick, it won't be # loaded and cause Coverage to try and get the current working # directory (see the comments above why this can be a problem) on OSX. d = utilFunc(self.exe, ['-S', '-u', scriptFile]) d.addCallback(check, dir.encode(sys.getfilesystemencoding())) return d def test_getProcessOutputDefaultPath(self): """ If no value is supplied for the C{path} parameter, L{getProcessOutput} runs the given command in the same working directory as the parent process and succeeds even if the current working directory is not accessible. """ return self._defaultPathTest(utils.getProcessOutput, self.assertEqual) def test_getProcessValueDefaultPath(self): """ If no value is supplied for the C{path} parameter, L{getProcessValue} runs the given command in the same working directory as the parent process and succeeds even if the current working directory is not accessible. """ def check(result, ignored): self.assertEqual(result, 0) return self._defaultPathTest(utils.getProcessValue, check) def test_getProcessOutputAndValueDefaultPath(self): """ If no value is supplied for the C{path} parameter, L{getProcessOutputAndValue} runs the given command in the same working directory as the parent process and succeeds even if the current working directory is not accessible. """ def check(out_err_status, dir): out, err, status = out_err_status self.assertEqual(out, dir) self.assertEqual(status, 0) return self._defaultPathTest(utils.getProcessOutputAndValue, check) def test_get_processOutputAndValueStdin(self): """ Standard input can be made available to the child process by passing bytes for the `stdinBytes` parameter. """ scriptFile = self.makeSourceFile([ "import sys", "sys.stdout.write(sys.stdin.read())", ]) stdinBytes = b"These are the bytes to see." d = utils.getProcessOutputAndValue( self.exe, ['-u', scriptFile], stdinBytes=stdinBytes, ) def gotOutputAndValue(out_err_code): out, err, code = out_err_code # Avoid making an exact equality comparison in case there is extra # random output on stdout (warnings, stray print statements, # logging, who knows). self.assertIn(stdinBytes, out) self.assertEqual(0, code) d.addCallback(gotOutputAndValue) return d
cryptography = requireModule("cryptography") unix = requireModule("twisted.conch.unix") if cryptography and pyasn1: try: from twisted.conch.scripts import cftp from twisted.conch.scripts.cftp import SSHSession from twisted.conch.ssh import filetransfer from twisted.conch.test import test_conch, test_ssh from twisted.conch.test.test_conch import FakeStdio from twisted.conch.test.test_filetransfer import FileTransferForTestAvatar except ImportError: pass skipTests = False if None in (unix, cryptography, pyasn1, interfaces.IReactorProcess(reactor, None)): skipTests = True @skipIf(skipTests, "don't run w/o spawnProcess or cryptography or pyasn1") class SSHSessionTests(TestCase): """ Tests for L{twisted.conch.scripts.cftp.SSHSession}. """ def test_eofReceived(self): """ L{twisted.conch.scripts.cftp.SSHSession.eofReceived} loses the write half of its stdio connection. """ stdio = FakeStdio()
def test_stderr(self): """ProcessProtocol.transport.closeStderr actually closes the pipe.""" d = self.doit(2) def _check(errput): # there should be no stderr open, so nothing for it to # write the error to. self.failUnlessEqual(errput, '') d.addCallback(_check) return d skipMessage = "wrong platform or reactor doesn't support IReactorProcess" if (runtime.platform.getType() != 'posix') or (not interfaces.IReactorProcess( reactor, None)): PosixProcessTestCase.skip = skipMessage PosixProcessTestCasePTY.skip = skipMessage TestTwoProcessesPosix.skip = skipMessage FDTest.skip = skipMessage else: # do this before running the tests: it uses SIGCHLD and stuff internally lsOut = popen2.popen3( "/bin/ls ZZXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")[2].read() if (runtime.platform.getType() != 'win32') or (not interfaces.IReactorProcess( reactor, None)): Win32ProcessTestCase.skip = skipMessage TestTwoProcessesNonPosix.skip = skipMessage if not interfaces.IReactorProcess(reactor, None):
# make sure that a) the old command's output doesn't interfere with # the new one, and b) the old command's actual termination doesn't # break anything args = { 'command': [sys.executable, self.sleepcmd, "5"], 'workdir': '.', 'timeout': 20 } c = SlaveShellCommand(self.builder, None, args) d = c.start() d.addCallback(self._testInterrupt2_2) return d def _testInterrupt2_2(self, res): self.checkrc(0) # N.B.: under windows, the trial process hangs out for another few # seconds. I assume that the win32eventreactor is waiting for one of # the lingering child processes to really finish. haveProcess = interfaces.IReactorProcess(reactor, None) if runtime.platformType == 'posix': # test with PTYs also class ShellPTY(ShellBase, unittest.TestCase): usePTY = True if not haveProcess: ShellPTY.skip = "this reactor doesn't support IReactorProcess" if not haveProcess: Shell.skip = "this reactor doesn't support IReactorProcess"
class ProcessStreamerTest(unittest.TestCase): if interfaces.IReactorProcess(reactor, None) is None: skip = "Platform lacks spawnProcess support, can't test process streaming." def runCode(self, code, inputStream=None): if inputStream is None: inputStream = stream.MemoryStream("") return stream.ProcessStreamer(inputStream, sys.executable, [sys.executable, "-u", "-c", code], os.environ) def test_output(self): p = self.runCode( "import sys\nfor i in range(100): sys.stdout.write('x' * 1000)") l = [] d = stream.readStream(p.outStream, l.append) def verify(_): self.assertEquals("".join(l), ("x" * 1000) * 100) d2 = p.run() return d.addCallback(verify).addCallback(lambda _: d2) def test_errouput(self): p = self.runCode( "import sys\nfor i in range(100): sys.stderr.write('x' * 1000)") l = [] d = stream.readStream(p.errStream, l.append) def verify(_): self.assertEquals("".join(l), ("x" * 1000) * 100) p.run() return d.addCallback(verify) def test_input(self): p = self.runCode("import sys\nsys.stdout.write(sys.stdin.read())", "hello world") l = [] d = stream.readStream(p.outStream, l.append) d2 = p.run() def verify(_): self.assertEquals("".join(l), "hello world") return d2 return d.addCallback(verify) def test_badexit(self): p = self.runCode("raise ValueError") l = [] from twisted.internet.error import ProcessTerminated def verify(_): self.assertEquals(l, [1]) self.assert_(p.outStream.closed) self.assert_(p.errStream.closed) return p.run().addErrback(lambda _: _.trap(ProcessTerminated) and l. append(1)).addCallback(verify) def test_inputerror(self): p = self.runCode( "import sys\nsys.stdout.write(sys.stdin.read())", TestStreamer(["hello", defer.fail(ZeroDivisionError())])) l = [] d = stream.readStream(p.outStream, l.append) d2 = p.run() def verify(_): self.assertEquals("".join(l), "hello") return d2 return d.addCallback(verify).addCallback( lambda _: log.flushErrors(ZeroDivisionError)) def test_processclosedinput(self): p = self.runCode( "import sys; sys.stdout.write(sys.stdin.read(3));" + "sys.stdin.close(); sys.stdout.write('def')", "abc123") l = [] d = stream.readStream(p.outStream, l.append) def verify(_): self.assertEquals("".join(l), "abcdef") d2 = p.run() return d.addCallback(verify).addCallback(lambda _: d2)
def connectionMade(self): self.output = [] self.error = [] def outReceived(self, out): self.output.append(out) def errReceived(self, err): self.error.append(err) def processEnded(self, reason): self.onCompletion.callback((reason, self.output, self.error)) self.onCompletion = None @skipIf(not interfaces.IReactorProcess(reactor, None), "cannot run test: reactor doesn't support IReactorProcess") class ResolveTests(unittest.TestCase): def testChildResolve(self): # I've seen problems with reactor.run under gtk2reactor. Spawn a # child which just does reactor.resolve after the reactor has # started, fail if it does not complete in a timely fashion. helperPath = os.path.abspath(self.mktemp()) with open(helperPath, 'w') as helperFile: # Eeueuuggg reactorName = reactor.__module__ helperFile.write(resolve_helper % {'reactor': reactorName}) env = os.environ.copy()