~certify-web-dev/twisted/certify-staging

« back to all changes in this revision

Viewing changes to twisted/internet/test/test_process.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2010-01-02 19:38:17 UTC
  • mfrom: (2.2.4 sid)
  • Revision ID: james.westby@ubuntu.com-20100102193817-jphp464ppwh7dulg
Tags: 9.0.0-1
* python-twisted: Depend on the python-twisted-* 9.0 packages.
* python-twisted: Depend on python-zope.interface only. Closes: #557781.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (c) 2008 Twisted Matrix Laboratories.
 
1
# Copyright (c) 2008-2009 Twisted Matrix Laboratories.
2
2
# See LICENSE for details.
3
3
 
4
4
"""
7
7
 
8
8
__metaclass__ = type
9
9
 
 
10
import os
10
11
import warnings, sys, signal
11
12
 
12
13
from twisted.internet.test.reactormixins import ReactorBuilder
15
16
from twisted.python.runtime import platform
16
17
from twisted.python.filepath import FilePath
17
18
from twisted.python.failure import Failure
18
 
from twisted.internet.defer import Deferred
 
19
from twisted.internet import utils
 
20
from twisted.internet.defer import Deferred, succeed
19
21
from twisted.internet.protocol import ProcessProtocol
20
22
from twisted.internet.error import ProcessDone, PotentialZombieWarning
21
23
from twisted.internet.error import ProcessTerminated
22
24
 
 
25
skipWindowsNopywin32 = None
 
26
if platform.isWindows():
 
27
    try:
 
28
        import win32process
 
29
    except ImportError:
 
30
        skipWindowsNopywin32 = ("On windows, spawnProcess is not available "
 
31
                                "in the absence of win32process.")
 
32
 
 
33
class _ShutdownCallbackProcessProtocol(ProcessProtocol):
 
34
    """
 
35
    An L{IProcessProtocol} which fires a Deferred when the process it is
 
36
    associated with ends.
 
37
    """
 
38
    def __init__(self, whenFinished):
 
39
        self.whenFinished = whenFinished
 
40
 
 
41
 
 
42
    def processEnded(self, reason):
 
43
        self.whenFinished.callback(None)
 
44
 
 
45
 
23
46
 
24
47
class ProcessTestsBuilderBase(ReactorBuilder):
25
48
    def spawnProcess(self, reactor):
27
50
        Call C{reactor.spawnProcess} with some simple arguments.  Do this here
28
51
        so that code object referenced by the stack frame has a C{co_filename}
29
52
        attribute set to this file so that L{TestCase.assertWarns} can be used.
 
53
 
 
54
        Return a L{Deferred} which fires when the process has exited.
30
55
        """
 
56
        onConnection = Deferred()
31
57
        reactor.spawnProcess(
32
 
            ProcessProtocol(), sys.executable, [sys.executable, "-c", ""],
33
 
            usePTY=self.usePTY)
 
58
            _ShutdownCallbackProcessProtocol(onConnection), sys.executable,
 
59
            [sys.executable, "-c", ""], usePTY=self.usePTY)
 
60
        return onConnection
34
61
 
35
62
 
36
63
    def test_spawnProcessTooEarlyWarns(self):
43
70
        warning and this test.
44
71
        """
45
72
        reactor = self.buildReactor()
46
 
        self.assertWarns(
 
73
        whenFinished = self.assertWarns(
47
74
            PotentialZombieWarning,
48
75
            PotentialZombieWarning.MESSAGE, __file__,
49
76
            self.spawnProcess, reactor)
50
77
 
 
78
        def check():
 
79
            # Handle the exact problem the warning we're testing for is about.
 
80
            # If the SIGCHLD arrives before we install our SIGCHLD handler,
 
81
            # we'll never see the process exit.  So after the SIGCHLD handler
 
82
            # is installed, try to reap children, just in case.
 
83
            from twisted.internet.process import reapAllProcesses
 
84
            reapAllProcesses()
 
85
        reactor.callWhenRunning(check)
 
86
 
 
87
        # Now wait for the process to exit before finishing the test.  If we
 
88
        # don't do this, tearDown might close the PTY before the child is done
 
89
        # with it.
 
90
        whenFinished.addCallback(
 
91
            lambda ignored: reactor.callWhenRunning(reactor.stop))
 
92
 
 
93
        self.runReactor(reactor)
 
94
 
51
95
 
52
96
    def test_callWhenRunningSpawnProcessWarningFree(self):
53
97
        """
54
98
        L{PotentialZombieWarning} is not emitted when the reactor is run after
55
99
        C{reactor.callWhenRunning(reactor.spawnProcess, ...)} has been called.
56
100
        """
57
 
        events = []
58
 
        self.patch(warnings, 'warn', lambda *a, **kw: events.append(a))
59
101
        reactor = self.buildReactor()
60
 
        reactor.callWhenRunning(self.spawnProcess, reactor)
61
 
        reactor.callWhenRunning(reactor.stop)
 
102
        def spawnProcess():
 
103
            whenFinished = self.spawnProcess(reactor)
 
104
            # Wait for the process to exit before finishing the test.  If we
 
105
            # don't do this, tearDown might close the PTY before the child is
 
106
            # done with it.
 
107
            whenFinished.addCallback(lambda ign: reactor.stop())
 
108
        reactor.callWhenRunning(spawnProcess)
62
109
        self.runReactor(reactor)
63
 
        self.assertFalse(events)
 
110
        self.assertEqual(self.flushWarnings(), [])
64
111
 
65
112
 
66
113
    if getattr(signal, 'SIGCHLD', None) is None:
302
349
        self.runReactor(reactor)
303
350
 
304
351
 
 
352
    def makeSourceFile(self, sourceLines):
 
353
        """
 
354
        Write the given list of lines to a text file and return the absolute
 
355
        path to it.
 
356
        """
 
357
        script = self.mktemp()
 
358
        scriptFile = file(script, 'wt')
 
359
        scriptFile.write(os.linesep.join(sourceLines) + os.linesep)
 
360
        scriptFile.close()
 
361
        return os.path.abspath(script)
 
362
 
 
363
 
 
364
    def test_shebang(self):
 
365
        """
 
366
        Spawning a process with an executable which is a script starting
 
367
        with an interpreter definition line (#!) uses that interpreter to
 
368
        evaluate the script.
 
369
        """
 
370
        SHEBANG_OUTPUT = 'this is the shebang output'
 
371
 
 
372
        scriptFile = self.makeSourceFile([
 
373
                "#!%s" % (sys.executable,),
 
374
                "import sys",
 
375
                "sys.stdout.write('%s')" % (SHEBANG_OUTPUT,),
 
376
                "sys.stdout.flush()"])
 
377
        os.chmod(scriptFile, 0700)
 
378
 
 
379
        reactor = self.buildReactor()
 
380
 
 
381
        def cbProcessExited((out, err, code)):
 
382
            msg("cbProcessExited((%r, %r, %d))" % (out, err, code))
 
383
            self.assertEqual(out, SHEBANG_OUTPUT)
 
384
            self.assertEqual(err, "")
 
385
            self.assertEqual(code, 0)
 
386
 
 
387
        def shutdown(passthrough):
 
388
            reactor.stop()
 
389
            return passthrough
 
390
 
 
391
        def start():
 
392
            d = utils.getProcessOutputAndValue(scriptFile, reactor=reactor)
 
393
            d.addBoth(shutdown)
 
394
            d.addCallback(cbProcessExited)
 
395
            d.addErrback(err)
 
396
 
 
397
        reactor.callWhenRunning(start)
 
398
        self.runReactor(reactor)
 
399
 
 
400
 
 
401
    def test_processCommandLineArguments(self):
 
402
        """
 
403
        Arguments given to spawnProcess are passed to the child process as
 
404
        originally intended.
 
405
        """
 
406
        source = (
 
407
            # On Windows, stdout is not opened in binary mode by default,
 
408
            # so newline characters are munged on writing, interfering with
 
409
            # the tests.
 
410
            'import sys, os\n'
 
411
            'try:\n'
 
412
            '  import msvcrt\n'
 
413
            '  msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)\n'
 
414
            'except ImportError:\n'
 
415
            '  pass\n'
 
416
            'for arg in sys.argv[1:]:\n'
 
417
            '  sys.stdout.write(arg + chr(0))\n'
 
418
            '  sys.stdout.flush()')
 
419
 
 
420
        args = ['hello', '"', ' \t|<>^&', r'"\\"hello\\"', r'"foo\ bar baz\""']
 
421
        # Ensure that all non-NUL characters can be passed too.
 
422
        args.append(''.join(map(chr, xrange(1, 256))))
 
423
 
 
424
        reactor = self.buildReactor()
 
425
 
 
426
        def processFinished(output):
 
427
            output = output.split('\0')
 
428
            # Drop the trailing \0.
 
429
            output.pop()
 
430
            self.assertEquals(args, output)
 
431
 
 
432
        def shutdown(result):
 
433
            reactor.stop()
 
434
            return result
 
435
 
 
436
        def spawnChild():
 
437
            d = succeed(None)
 
438
            d.addCallback(lambda dummy: utils.getProcessOutput(
 
439
                sys.executable, ['-c', source] + args, reactor=reactor))
 
440
            d.addCallback(processFinished)
 
441
            d.addBoth(shutdown)
 
442
 
 
443
        reactor.callWhenRunning(spawnChild)
 
444
        self.runReactor(reactor)
 
445
 
 
446
 
 
447
 
 
448
ProcessTestsBuilder.skip = skipWindowsNopywin32
305
449
globals().update(ProcessTestsBuilder.makeTestCaseClasses())
306
450
 
307
451