Honour stdout on TestProgram more fully.

We were failing to pass it into the runner, and this causes hilarity
for child runners which then end up with sys.stdout in their test
  suite, rather than their stub streams.

Change-Id: I19984102547f92493f330059928f7e0b3897c38c
This commit is contained in:
Robert Collins
2014-08-24 17:05:39 +12:00
parent 7683940260
commit 07543a19b6
5 changed files with 48 additions and 12 deletions

6
NEWS
View File

@@ -10,6 +10,12 @@ NEXT
Changes Changes
------- -------
* ``stdout`` is now correctly honoured on ``run.TestProgram`` - before the
runner objects would be created with no stdout parameter. If construction
fails, the previous parameter list is attempted, permitting compatibility
with Runner classes that don't accept stdout as a parameter.
(Robert Collins)
* The ``ExtendedToStreamDecorator`` now handles content objects with one less * The ``ExtendedToStreamDecorator`` now handles content objects with one less
packet - the last packet of the source content is sent with EOF set rather packet - the last packet of the source content is sent with EOF set rather
than an empty packet with EOF set being sent after the last packet of the than an empty packet with EOF set being sent after the last packet of the

View File

@@ -26,7 +26,7 @@ class TestCommand(Command):
def __init__(self, dist): def __init__(self, dist):
Command.__init__(self, dist) Command.__init__(self, dist)
self.runner = TestToolsTestRunner(sys.stdout) self.runner = TestToolsTestRunner(stdout=sys.stdout)
def initialize_options(self): def initialize_options(self):

View File

@@ -73,6 +73,8 @@ class TestToolsTestRunner(object):
:param stdout: Stream to use for stdout. :param stdout: Stream to use for stdout.
""" """
self.failfast = failfast self.failfast = failfast
if stdout is None:
stdout = sys.stdout
self.stdout = stdout self.stdout = stdout
def list(self, test): def list(self, test):
@@ -89,7 +91,7 @@ class TestToolsTestRunner(object):
def run(self, test): def run(self, test):
"Run the given test case or test suite." "Run the given test case or test suite."
result = TextTestResult( result = TextTestResult(
unicode_output_stream(sys.stdout), failfast=self.failfast) unicode_output_stream(self.stdout), failfast=self.failfast)
result.startTestRun() result.startTestRun()
try: try:
return test.run(result) return test.run(result)
@@ -185,6 +187,7 @@ class TestProgram(object):
argv = sys.argv argv = sys.argv
if stdout is None: if stdout is None:
stdout = sys.stdout stdout = sys.stdout
self.stdout = stdout
self.exit = exit self.exit = exit
self.failfast = failfast self.failfast = failfast
@@ -224,7 +227,7 @@ class TestProgram(object):
runner.list(self.test) runner.list(self.test)
else: else:
for test in iterate_tests(self.test): for test in iterate_tests(self.test):
stdout.write('%s\n' % test.id()) self.stdout.write('%s\n' % test.id())
def usageExit(self, msg=None): def usageExit(self, msg=None):
if msg: if msg:
@@ -375,12 +378,20 @@ class TestProgram(object):
def _get_runner(self): def _get_runner(self):
if self.testRunner is None: if self.testRunner is None:
self.testRunner = TestToolsTestRunner self.testRunner = TestToolsTestRunner
try:
testRunner = self.testRunner(verbosity=self.verbosity,
failfast=self.failfast,
buffer=self.buffer,
stdout=self.stdout)
except TypeError:
# didn't accept the verbosity, buffer, failfast or stdout arguments
# Try with the prior contract
try: try:
testRunner = self.testRunner(verbosity=self.verbosity, testRunner = self.testRunner(verbosity=self.verbosity,
failfast=self.failfast, failfast=self.failfast,
buffer=self.buffer) buffer=self.buffer)
except TypeError: except TypeError:
# didn't accept the verbosity, buffer or failfast arguments # Now try calling it with defaults
try: try:
testRunner = self.testRunner() testRunner = self.testRunner()
except TypeError: except TypeError:

View File

@@ -61,8 +61,8 @@ class TestCommandTest(TestCase):
dist.cmdclass = {'test': TestCommand} dist.cmdclass = {'test': TestCommand}
dist.command_options = { dist.command_options = {
'test': {'test_module': ('command line', 'testtools.runexample')}} 'test': {'test_module': ('command line', 'testtools.runexample')}}
cmd = dist.reinitialize_command('test')
with fixtures.MonkeyPatch('sys.stdout', stdout.stream): with fixtures.MonkeyPatch('sys.stdout', stdout.stream):
cmd = dist.reinitialize_command('test')
dist.run_command('test') dist.run_command('test')
self.assertThat( self.assertThat(
stdout.getDetails()['stdout'].as_text(), stdout.getDetails()['stdout'].as_text(),
@@ -83,8 +83,8 @@ OK
'test': { 'test': {
'test_suite': ( 'test_suite': (
'command line', 'testtools.runexample.test_suite')}} 'command line', 'testtools.runexample.test_suite')}}
cmd = dist.reinitialize_command('test')
with fixtures.MonkeyPatch('sys.stdout', stdout.stream): with fixtures.MonkeyPatch('sys.stdout', stdout.stream):
cmd = dist.reinitialize_command('test')
dist.run_command('test') dist.run_command('test')
self.assertThat( self.assertThat(
stdout.getDetails()['stdout'].as_text(), stdout.getDetails()['stdout'].as_text(),

View File

@@ -13,9 +13,13 @@ import testtools
from testtools import TestCase, run from testtools import TestCase, run
from testtools.compat import ( from testtools.compat import (
_b, _b,
_u,
StringIO, StringIO,
) )
from testtools.matchers import Contains from testtools.matchers import (
Contains,
MatchesRegex,
)
if fixtures: if fixtures:
@@ -235,12 +239,27 @@ testtools.resourceexample.TestFoo.test_foo
self.fail('a') self.fail('a')
def test_b(self): def test_b(self):
self.fail('b') self.fail('b')
runner = run.TestToolsTestRunner(failfast=True)
with fixtures.MonkeyPatch('sys.stdout', stdout.stream): with fixtures.MonkeyPatch('sys.stdout', stdout.stream):
runner = run.TestToolsTestRunner(failfast=True)
runner.run(TestSuite([Failing('test_a'), Failing('test_b')])) runner.run(TestSuite([Failing('test_a'), Failing('test_b')]))
self.assertThat( self.assertThat(
stdout.getDetails()['stdout'].as_text(), Contains('Ran 1 test')) stdout.getDetails()['stdout'].as_text(), Contains('Ran 1 test'))
def test_stdout_honoured(self):
self.useFixture(SampleTestFixture())
tests = []
out = StringIO()
exc = self.assertRaises(SystemExit, run.main,
argv=['prog', 'testtools.runexample.test_suite'],
stdout=out)
self.assertEqual((0,), exc.args)
self.assertThat(
out.getvalue(),
MatchesRegex(_u("""Tests running...
Ran 2 tests in \\d.\\d\\d\\ds
OK
""")))
def test_suite(): def test_suite():