
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
# See LICENSE for details.


"""
Test running processes.
"""

import warnings, os, sys, signal

# Twisted Imports
from twisted.trial import unittest
from twisted.test.test_process import SignalMixin

from twisted.internet import reactor, utils, interfaces


class UtilsTestCase(SignalMixin, unittest.TestCase):
    """Test running a process."""

    output = None
    value = None

    def makeSourceFile(self, sourceLines):
        script = self.mktemp()
        scriptFile = file(script, 'wt')
        scriptFile.write(os.linesep.join(sourceLines) + os.linesep)
        scriptFile.close()
        return script

    def testOutput(self):
        scriptFile = self.makeSourceFile([
            'print "hello world"'
            ])
        d = utils.getProcessOutput(sys.executable, ['-u', scriptFile])
        return d.addCallback(self.assertEquals, "hello world\n")

    def testOutputWithErrorIgnored(self):
        # make sure stderr raises an error normally
        exe = sys.executable
        scriptFile = self.makeSourceFile([
            'import sys',
            'sys.stderr.write("hello world\\n")'
            ])

        d = utils.getProcessOutput(exe, ['-u', scriptFile], errortoo=0)
        return self.assertFailure(d, IOError)

    def testOutputWithErrorCollected(self):
        # make sure stderr raises an error normally
        exe = sys.executable
        scriptFile = self.makeSourceFile([
            'import sys',
            'sys.stderr.write("hello world\\n")'
            ])

        d = utils.getProcessOutput(exe, ['-u', scriptFile], errortoo=1)
        return d.addCallback(self.assertEquals, "hello world" + os.linesep)

    def testValue(self):
        exe = sys.executable
        scriptFile = self.makeSourceFile([
            "import sys",
            "sys.exit(1)"
            ])

        d = utils.getProcessValue(exe, ['-u', scriptFile])
        return d.addCallback(self.assertEquals, 1)

    def testOutputAndValue(self):
        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.assertEquals(out, "hello world!" + os.linesep)
            self.assertEquals(err, "goodbye world!" + os.linesep)
            self.assertEquals(code, 1)
        d = utils.getProcessOutputAndValue(exe, [scriptFile])
        return d.addCallback(gotOutputAndValue)

    def testOutputSignal(self):
        # 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.
        exe = sys.executable
        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(err):
            (out, err, sig) = err.value # XXX Sigh wtf
            self.assertEquals(out, "stdout bytes" + os.linesep)
            self.assertEquals(err, "stderr bytes" + os.linesep)
            self.assertEquals(sig, signal.SIGKILL)

        d = utils.getProcessOutputAndValue(exe, ['-u', scriptFile])
        return d.addErrback(gotOutputAndValue)



class WarningSuppression(unittest.TestCase):
    def setUp(self):
        self.warnings = []
        self.originalshow = warnings.showwarning
        warnings.showwarning = self.showwarning


    def tearDown(self):
        warnings.showwarning = self.originalshow


    def showwarning(self, *a, **kw):
        self.warnings.append((a, kw))


    def testSuppressWarnings(self):
        def f(msg):
            warnings.warn(msg)
        g = utils.suppressWarnings(f, (('ignore',), dict(message="This is message")))

        # Start off with a sanity check - calling the original function
        # should emit the warning.
        f("Sanity check message")
        self.assertEquals(len(self.warnings), 1)

        # Now that that's out of the way, call the wrapped function, and
        # make sure no new warnings show up.
        g("This is message")
        self.assertEquals(len(self.warnings), 1)

        # Finally, emit another warning which should not be ignored, and
        # make sure it is not.
        g("Unignored message")
        self.assertEquals(len(self.warnings), 2)



if interfaces.IReactorProcess(reactor, None) is None:
    UtilsTestCase.skip = "reactor doesn't implement IReactorProcess"
