Skip to content

Commit

Permalink
bpo-13236: Flush the output stream more often in unittest (GH-29864)
Browse files Browse the repository at this point in the history
It can prevent some losses when output to buffered stream.
  • Loading branch information
serhiy-storchaka committed Dec 4, 2021
1 parent 87a18de commit f42a06b
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 5 deletions.
6 changes: 6 additions & 0 deletions Lib/unittest/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def _write_status(self, test, status):
self.stream.write(self.getDescription(test))
self.stream.write(" ... ")
self.stream.writeln(status)
self.stream.flush()
self._newline = True

def addSubTest(self, test, subtest, err):
Expand Down Expand Up @@ -121,6 +122,7 @@ def addExpectedFailure(self, test, err):
super(TextTestResult, self).addExpectedFailure(test, err)
if self.showAll:
self.stream.writeln("expected failure")
self.stream.flush()
elif self.dots:
self.stream.write("x")
self.stream.flush()
Expand All @@ -129,13 +131,15 @@ def addUnexpectedSuccess(self, test):
super(TextTestResult, self).addUnexpectedSuccess(test)
if self.showAll:
self.stream.writeln("unexpected success")
self.stream.flush()
elif self.dots:
self.stream.write("u")
self.stream.flush()

def printErrors(self):
if self.dots or self.showAll:
self.stream.writeln()
self.stream.flush()
self.printErrorList('ERROR', self.errors)
self.printErrorList('FAIL', self.failures)

Expand All @@ -145,6 +149,7 @@ def printErrorList(self, flavour, errors):
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
self.stream.writeln(self.separator2)
self.stream.writeln("%s" % err)
self.stream.flush()


class TextTestRunner(object):
Expand Down Expand Up @@ -239,4 +244,5 @@ def run(self, test):
self.stream.writeln(" (%s)" % (", ".join(infos),))
else:
self.stream.write("\n")
self.stream.flush()
return result
16 changes: 13 additions & 3 deletions Lib/unittest/test/test_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from test import support
import unittest
import unittest.test
from .test_result import BufferedWriter


class Test_TestProgram(unittest.TestCase):
Expand Down Expand Up @@ -104,30 +105,39 @@ def run(self, test):
program.testNames)

def test_NonExit(self):
stream = BufferedWriter()
program = unittest.main(exit=False,
argv=["foobar"],
testRunner=unittest.TextTestRunner(stream=io.StringIO()),
testRunner=unittest.TextTestRunner(stream=stream),
testLoader=self.FooBarLoader())
self.assertTrue(hasattr(program, 'result'))
self.assertIn('\nFAIL: testFail ', stream.getvalue())
self.assertTrue(stream.getvalue().endswith('\n\nFAILED (failures=1)\n'))


def test_Exit(self):
stream = BufferedWriter()
self.assertRaises(
SystemExit,
unittest.main,
argv=["foobar"],
testRunner=unittest.TextTestRunner(stream=io.StringIO()),
testRunner=unittest.TextTestRunner(stream=stream),
exit=True,
testLoader=self.FooBarLoader())
self.assertIn('\nFAIL: testFail ', stream.getvalue())
self.assertTrue(stream.getvalue().endswith('\n\nFAILED (failures=1)\n'))


def test_ExitAsDefault(self):
stream = BufferedWriter()
self.assertRaises(
SystemExit,
unittest.main,
argv=["foobar"],
testRunner=unittest.TextTestRunner(stream=io.StringIO()),
testRunner=unittest.TextTestRunner(stream=stream),
testLoader=self.FooBarLoader())
self.assertIn('\nFAIL: testFail ', stream.getvalue())
self.assertTrue(stream.getvalue().endswith('\n\nFAILED (failures=1)\n'))


class InitialisableProgram(unittest.TestProgram):
Expand Down
35 changes: 33 additions & 2 deletions Lib/unittest/test/test_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@ def bad_cleanup2():
raise ValueError('bad cleanup2')


class BufferedWriter:
def __init__(self):
self.result = ''
self.buffer = ''

def write(self, arg):
self.buffer += arg

def flush(self):
self.result += self.buffer
self.buffer = ''

def getvalue(self):
return self.result


class Test_TestResult(unittest.TestCase):
# Note: there are not separate tests for TestResult.wasSuccessful(),
# TestResult.errors, TestResult.failures, TestResult.testsRun or
Expand Down Expand Up @@ -335,10 +351,13 @@ def testFailFast(self):
self.assertTrue(result.shouldStop)

def testFailFastSetByRunner(self):
runner = unittest.TextTestRunner(stream=io.StringIO(), failfast=True)
stream = BufferedWriter()
runner = unittest.TextTestRunner(stream=stream, failfast=True)
def test(result):
self.assertTrue(result.failfast)
result = runner.run(test)
stream.flush()
self.assertTrue(stream.getvalue().endswith('\n\nOK\n'))


class Test_TextTestResult(unittest.TestCase):
Expand Down Expand Up @@ -462,6 +481,12 @@ def testFail(self):
self.fail('fail')
def testError(self):
raise Exception('error')
@unittest.expectedFailure
def testExpectedFailure(self):
self.fail('fail')
@unittest.expectedFailure
def testUnexpectedSuccess(self):
pass
def testSubTestSuccess(self):
with self.subTest('one', a=1):
pass
Expand All @@ -483,7 +508,7 @@ def tearDown(self):
raise self.tearDownError

def _run_test(self, test_name, verbosity, tearDownError=None):
stream = io.StringIO()
stream = BufferedWriter()
stream = unittest.runner._WritelnDecorator(stream)
result = unittest.TextTestResult(stream, True, verbosity)
test = self.Test(test_name)
Expand All @@ -496,6 +521,8 @@ def testDotsOutput(self):
self.assertEqual(self._run_test('testSkip', 1), 's')
self.assertEqual(self._run_test('testFail', 1), 'F')
self.assertEqual(self._run_test('testError', 1), 'E')
self.assertEqual(self._run_test('testExpectedFailure', 1), 'x')
self.assertEqual(self._run_test('testUnexpectedSuccess', 1), 'u')

def testLongOutput(self):
classname = f'{__name__}.{self.Test.__qualname__}'
Expand All @@ -507,6 +534,10 @@ def testLongOutput(self):
f'testFail ({classname}) ... FAIL\n')
self.assertEqual(self._run_test('testError', 2),
f'testError ({classname}) ... ERROR\n')
self.assertEqual(self._run_test('testExpectedFailure', 2),
f'testExpectedFailure ({classname}) ... expected failure\n')
self.assertEqual(self._run_test('testUnexpectedSuccess', 2),
f'testUnexpectedSuccess ({classname}) ... unexpected success\n')

def testDotsOutputSubTestSuccess(self):
self.assertEqual(self._run_test('testSubTestSuccess', 1), '.')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:class:`unittest.TextTestResult` and :class:`unittest.TextTestRunner` flush
now the output stream more often.

0 comments on commit f42a06b

Please sign in to comment.