From 07543a19b68fb35cf9fd5bc241bed2afb4b22ecc Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Sun, 24 Aug 2014 17:05:39 +1200 Subject: [PATCH] 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 --- NEWS | 6 ++++++ testtools/distutilscmd.py | 2 +- testtools/run.py | 25 ++++++++++++++++++------- testtools/tests/test_distutilscmd.py | 4 ++-- testtools/tests/test_run.py | 23 +++++++++++++++++++++-- 5 files changed, 48 insertions(+), 12 deletions(-) diff --git a/NEWS b/NEWS index 2ddabf9..3f81f06 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,12 @@ NEXT 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 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 diff --git a/testtools/distutilscmd.py b/testtools/distutilscmd.py index 91e14ca..a4d79dc 100644 --- a/testtools/distutilscmd.py +++ b/testtools/distutilscmd.py @@ -26,7 +26,7 @@ class TestCommand(Command): def __init__(self, dist): Command.__init__(self, dist) - self.runner = TestToolsTestRunner(sys.stdout) + self.runner = TestToolsTestRunner(stdout=sys.stdout) def initialize_options(self): diff --git a/testtools/run.py b/testtools/run.py index 466da76..a8e1685 100755 --- a/testtools/run.py +++ b/testtools/run.py @@ -73,6 +73,8 @@ class TestToolsTestRunner(object): :param stdout: Stream to use for stdout. """ self.failfast = failfast + if stdout is None: + stdout = sys.stdout self.stdout = stdout def list(self, test): @@ -89,7 +91,7 @@ class TestToolsTestRunner(object): def run(self, test): "Run the given test case or test suite." result = TextTestResult( - unicode_output_stream(sys.stdout), failfast=self.failfast) + unicode_output_stream(self.stdout), failfast=self.failfast) result.startTestRun() try: return test.run(result) @@ -185,6 +187,7 @@ class TestProgram(object): argv = sys.argv if stdout is None: stdout = sys.stdout + self.stdout = stdout self.exit = exit self.failfast = failfast @@ -224,7 +227,7 @@ class TestProgram(object): runner.list(self.test) else: 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): if msg: @@ -378,14 +381,22 @@ class TestProgram(object): try: testRunner = self.testRunner(verbosity=self.verbosity, failfast=self.failfast, - buffer=self.buffer) + buffer=self.buffer, + stdout=self.stdout) except TypeError: - # didn't accept the verbosity, buffer or failfast arguments + # didn't accept the verbosity, buffer, failfast or stdout arguments + # Try with the prior contract try: - testRunner = self.testRunner() + testRunner = self.testRunner(verbosity=self.verbosity, + failfast=self.failfast, + buffer=self.buffer) except TypeError: - # it is assumed to be a TestRunner instance - testRunner = self.testRunner + # Now try calling it with defaults + try: + testRunner = self.testRunner() + except TypeError: + # it is assumed to be a TestRunner instance + testRunner = self.testRunner return testRunner diff --git a/testtools/tests/test_distutilscmd.py b/testtools/tests/test_distutilscmd.py index 7bfc1fa..fd0dd90 100644 --- a/testtools/tests/test_distutilscmd.py +++ b/testtools/tests/test_distutilscmd.py @@ -61,8 +61,8 @@ class TestCommandTest(TestCase): dist.cmdclass = {'test': TestCommand} dist.command_options = { 'test': {'test_module': ('command line', 'testtools.runexample')}} - cmd = dist.reinitialize_command('test') with fixtures.MonkeyPatch('sys.stdout', stdout.stream): + cmd = dist.reinitialize_command('test') dist.run_command('test') self.assertThat( stdout.getDetails()['stdout'].as_text(), @@ -83,8 +83,8 @@ OK 'test': { 'test_suite': ( 'command line', 'testtools.runexample.test_suite')}} - cmd = dist.reinitialize_command('test') with fixtures.MonkeyPatch('sys.stdout', stdout.stream): + cmd = dist.reinitialize_command('test') dist.run_command('test') self.assertThat( stdout.getDetails()['stdout'].as_text(), diff --git a/testtools/tests/test_run.py b/testtools/tests/test_run.py index e89ecdc..9350944 100644 --- a/testtools/tests/test_run.py +++ b/testtools/tests/test_run.py @@ -13,9 +13,13 @@ import testtools from testtools import TestCase, run from testtools.compat import ( _b, + _u, StringIO, ) -from testtools.matchers import Contains +from testtools.matchers import ( + Contains, + MatchesRegex, + ) if fixtures: @@ -235,12 +239,27 @@ testtools.resourceexample.TestFoo.test_foo self.fail('a') def test_b(self): self.fail('b') - runner = run.TestToolsTestRunner(failfast=True) with fixtures.MonkeyPatch('sys.stdout', stdout.stream): + runner = run.TestToolsTestRunner(failfast=True) runner.run(TestSuite([Failing('test_a'), Failing('test_b')])) self.assertThat( 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():