summaryrefslogtreecommitdiffstats
path: root/Lib/unittest
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/unittest')
-rw-r--r--Lib/unittest/case.py71
-rw-r--r--Lib/unittest/loader.py39
-rw-r--r--Lib/unittest/main.py41
-rw-r--r--Lib/unittest/result.py64
-rw-r--r--Lib/unittest/runner.py4
-rw-r--r--Lib/unittest/test/dummy.py1
-rw-r--r--Lib/unittest/test/test_assertions.py34
-rw-r--r--Lib/unittest/test/test_break.py10
-rw-r--r--Lib/unittest/test/test_discovery.py11
-rw-r--r--Lib/unittest/test/test_loader.py20
-rw-r--r--Lib/unittest/test/test_result.py132
11 files changed, 373 insertions, 54 deletions
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py
index ac5d1ec..366fe87 100644
--- a/Lib/unittest/case.py
+++ b/Lib/unittest/case.py
@@ -502,10 +502,12 @@ class TestCase(object):
safe_repr(second)))
raise self.failureException(msg)
- def assertAlmostEqual(self, first, second, *, places=7, msg=None):
+ def assertAlmostEqual(self, first, second, *, places=None, msg=None,
+ delta=None):
"""Fail if the two objects are unequal as determined by their
difference rounded to the given number of decimal places
- (default 7) and comparing to zero.
+ (default 7) and comparing to zero, or by comparing that the
+ between the two objects is more than the given delta.
Note that decimal places (from zero) are usually not the same
as significant digits (measured from the most signficant digit).
@@ -514,31 +516,62 @@ class TestCase(object):
compare almost equal.
"""
if first == second:
- # shortcut for inf
+ # shortcut
return
- if round(abs(second-first), places) != 0:
+ if delta is not None and places is not None:
+ raise TypeError("specify delta or places not both")
+
+ if delta is not None:
+ if abs(first - second) <= delta:
+ return
+
+ standardMsg = '%s != %s within %s delta' % (safe_repr(first),
+ safe_repr(second),
+ safe_repr(delta))
+ else:
+ if places is None:
+ places = 7
+
+ if round(abs(second-first), places) == 0:
+ return
+
standardMsg = '%s != %s within %r places' % (safe_repr(first),
safe_repr(second),
places)
- msg = self._formatMessage(msg, standardMsg)
- raise self.failureException(msg)
+ msg = self._formatMessage(msg, standardMsg)
+ raise self.failureException(msg)
- def assertNotAlmostEqual(self, first, second, *, places=7, msg=None):
+ def assertNotAlmostEqual(self, first, second, *, places=None, msg=None,
+ delta=None):
"""Fail if the two objects are equal as determined by their
difference rounded to the given number of decimal places
- (default 7) and comparing to zero.
+ (default 7) and comparing to zero, or by comparing that the
+ between the two objects is less than the given delta.
Note that decimal places (from zero) are usually not the same
as significant digits (measured from the most signficant digit).
Objects that are equal automatically fail.
"""
- if (first == second) or round(abs(second-first), places) == 0:
+ if delta is not None and places is not None:
+ raise TypeError("specify delta or places not both")
+ if delta is not None:
+ if not (first == second) and abs(first - second) > delta:
+ return
+ standardMsg = '%s == %s within %s delta' % (safe_repr(first),
+ safe_repr(second),
+ safe_repr(delta))
+ else:
+ if places is None:
+ places = 7
+ if not (first == second) and round(abs(second-first), places) != 0:
+ return
standardMsg = '%s == %s within %r places' % (safe_repr(first),
- safe_repr(second),
- places)
- msg = self._formatMessage(msg, standardMsg)
- raise self.failureException(msg)
+ safe_repr(second),
+ places)
+
+ msg = self._formatMessage(msg, standardMsg)
+ raise self.failureException(msg)
# Synonyms for assertion methods
@@ -967,6 +1000,18 @@ class TestCase(object):
msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text)
raise self.failureException(msg)
+ def assertNotRegexpMatches(self, text, unexpected_regexp, msg=None):
+ if isinstance(unexpected_regexp, (str, bytes)):
+ unexpected_regexp = re.compile(unexpected_regexp)
+ match = unexpected_regexp.search(text)
+ if match:
+ msg = msg or "Regexp matched"
+ msg = '%s: %r matches %r in %r' % (msg,
+ text[match.start():match.end()],
+ unexpected_regexp.pattern,
+ text)
+ raise self.failureException(msg)
+
class FunctionTestCase(TestCase):
"""A test case that wraps a test function.
diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py
index f00f38d..a45dffa 100644
--- a/Lib/unittest/loader.py
+++ b/Lib/unittest/loader.py
@@ -166,27 +166,58 @@ class TestLoader(object):
packages can continue discovery themselves. top_level_dir is stored so
load_tests does not need to pass this argument in to loader.discover().
"""
+ set_implicit_top = False
if top_level_dir is None and self._top_level_dir is not None:
# make top_level_dir optional if called from load_tests in a package
top_level_dir = self._top_level_dir
elif top_level_dir is None:
+ set_implicit_top = True
top_level_dir = start_dir
- top_level_dir = os.path.abspath(os.path.normpath(top_level_dir))
- start_dir = os.path.abspath(os.path.normpath(start_dir))
+ top_level_dir = os.path.abspath(top_level_dir)
if not top_level_dir in sys.path:
# all test modules must be importable from the top level directory
sys.path.append(top_level_dir)
self._top_level_dir = top_level_dir
- if start_dir != top_level_dir and not os.path.isfile(os.path.join(start_dir, '__init__.py')):
- # what about __init__.pyc or pyo (etc)
+ is_not_importable = False
+ if os.path.isdir(os.path.abspath(start_dir)):
+ start_dir = os.path.abspath(start_dir)
+ if start_dir != top_level_dir:
+ is_not_importable = not os.path.isfile(os.path.join(start_dir, '__init__.py'))
+ else:
+ # support for discovery from dotted module names
+ try:
+ __import__(start_dir)
+ except ImportError:
+ is_not_importable = True
+ else:
+ the_module = sys.modules[start_dir]
+ top_part = start_dir.split('.')[0]
+ start_dir = os.path.abspath(os.path.dirname((the_module.__file__)))
+ if set_implicit_top:
+ self._top_level_dir = self._get_directory_containing_module(top_part)
+ sys.path.remove(top_level_dir)
+
+ if is_not_importable:
raise ImportError('Start directory is not importable: %r' % start_dir)
tests = list(self._find_tests(start_dir, pattern))
return self.suiteClass(tests)
+ def _get_directory_containing_module(self, module_name):
+ module = sys.modules[module_name]
+ full_path = os.path.abspath(module.__file__)
+
+ if os.path.basename(full_path).lower().startswith('__init__.py'):
+ return os.path.dirname(os.path.dirname(full_path))
+ else:
+ # here we have been given a module rather than a package - so
+ # all we can do is search the *same* directory the module is in
+ # should an exception be raised instead
+ return os.path.dirname(full_path)
+
def _get_name_from_path(self, path):
path = os.path.splitext(os.path.normpath(path))[0]
diff --git a/Lib/unittest/main.py b/Lib/unittest/main.py
index 3e659ab..2f488e1 100644
--- a/Lib/unittest/main.py
+++ b/Lib/unittest/main.py
@@ -9,9 +9,9 @@ from .signals import installHandler
__unittest = True
-
-FAILFAST = " -f, --failfast Stop on first failure\n"
-CATCHBREAK = " -c, --catch Catch control-C and display results\n"
+FAILFAST = " -f, --failfast Stop on first failure\n"
+CATCHBREAK = " -c, --catch Catch control-C and display results\n"
+BUFFEROUTPUT = " -b, --buffer Buffer stdout and stderr during test runs\n"
USAGE_AS_MAIN = """\
Usage: %(progName)s [options] [tests]
@@ -20,7 +20,7 @@ Options:
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
-%(failfast)s%(catchbreak)s
+%(failfast)s%(catchbreak)s%(buffer)s
Examples:
%(progName)s test_module - run tests from test_module
%(progName)s test_module.TestClass - run tests from
@@ -34,7 +34,7 @@ Alternative Usage: %(progName)s discover [options]
Options:
-v, --verbose Verbose output
-%(failfast)s%(catchbreak)s -s directory Directory to start discovery ('.' default)
+%(failfast)s%(catchbreak)s%(buffer)s -s directory Directory to start discovery ('.' default)
-p pattern Pattern to match test files ('test*.py' default)
-t directory Top level directory of project (default to
start directory)
@@ -50,7 +50,7 @@ Options:
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
-%(failfast)s%(catchbreak)s
+%(failfast)s%(catchbreak)s%(buffer)s
Examples:
%(progName)s - run default set of tests
%(progName)s MyTestSuite - run suite 'MyTestSuite'
@@ -68,12 +68,12 @@ class TestProgram(object):
USAGE = USAGE_FROM_MODULE
# defaults for testing
- failfast = catchbreak = None
+ failfast = catchbreak = buffer = None
def __init__(self, module='__main__', defaultTest=None,
argv=None, testRunner=None,
testLoader=loader.defaultTestLoader, exit=True,
- verbosity=1, failfast=None, catchbreak=None):
+ verbosity=1, failfast=None, catchbreak=None, buffer=None):
if isinstance(module, str):
self.module = __import__(module)
for part in module.split('.')[1:]:
@@ -87,6 +87,7 @@ class TestProgram(object):
self.failfast = failfast
self.catchbreak = catchbreak
self.verbosity = verbosity
+ self.buffer = buffer
self.defaultTest = defaultTest
self.testRunner = testRunner
self.testLoader = testLoader
@@ -97,11 +98,14 @@ class TestProgram(object):
def usageExit(self, msg=None):
if msg:
print(msg)
- usage = {'progName': self.progName, 'catchbreak': '', 'failfast': ''}
+ usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '',
+ 'buffer': ''}
if self.failfast != False:
usage['failfast'] = FAILFAST
if self.catchbreak != False:
usage['catchbreak'] = CATCHBREAK
+ if self.buffer != False:
+ usage['buffer'] = BUFFEROUTPUT
print(self.USAGE % usage)
sys.exit(2)
@@ -111,9 +115,9 @@ class TestProgram(object):
return
import getopt
- long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch']
+ long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer']
try:
- options, args = getopt.getopt(argv[1:], 'hHvqfc', long_opts)
+ options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts)
for opt, value in options:
if opt in ('-h','-H','--help'):
self.usageExit()
@@ -129,6 +133,10 @@ class TestProgram(object):
if self.catchbreak is None:
self.catchbreak = True
# Should this raise an exception if -c is not valid?
+ if opt in ('-b','--buffer'):
+ if self.buffer is None:
+ self.buffer = True
+ # Should this raise an exception if -b is not valid?
if len(args) == 0 and self.defaultTest is None:
# createTests will load tests from self.module
self.testNames = None
@@ -164,6 +172,10 @@ class TestProgram(object):
parser.add_option('-c', '--catch', dest='catchbreak', default=False,
help='Catch ctrl-C and display results so far',
action='store_true')
+ if self.buffer != False:
+ parser.add_option('-b', '--buffer', dest='buffer', default=False,
+ help='Buffer stdout and stderr during tests',
+ action='store_true')
parser.add_option('-s', '--start-directory', dest='start', default='.',
help="Directory to start discovery ('.' default)")
parser.add_option('-p', '--pattern', dest='pattern', default='test*.py',
@@ -184,6 +196,8 @@ class TestProgram(object):
self.failfast = options.failfast
if self.catchbreak is None:
self.catchbreak = options.catchbreak
+ if self.buffer is None:
+ self.buffer = options.buffer
if options.verbose:
self.verbosity = 2
@@ -203,9 +217,10 @@ class TestProgram(object):
if isinstance(self.testRunner, type):
try:
testRunner = self.testRunner(verbosity=self.verbosity,
- failfast=self.failfast)
+ failfast=self.failfast,
+ buffer=self.buffer)
except TypeError:
- # didn't accept the verbosity or failfast arguments
+ # didn't accept the verbosity, buffer or failfast arguments
testRunner = self.testRunner()
else:
# it is assumed to be a TestRunner instance
diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py
index ec80e8e..92b1f91 100644
--- a/Lib/unittest/result.py
+++ b/Lib/unittest/result.py
@@ -1,5 +1,8 @@
"""Test result object"""
+import os
+import io
+import sys
import traceback
from . import util
@@ -15,6 +18,10 @@ def failfast(method):
return method(self, *args, **kw)
return inner
+STDOUT_LINE = '\nStdout:\n%s'
+STDERR_LINE = '\nStderr:\n%s'
+
+
class TestResult(object):
"""Holder for test result information.
@@ -37,6 +44,12 @@ class TestResult(object):
self.expectedFailures = []
self.unexpectedSuccesses = []
self.shouldStop = False
+ self.buffer = False
+ self._stdout_buffer = None
+ self._stderr_buffer = None
+ self._original_stdout = sys.stdout
+ self._original_stderr = sys.stderr
+ self._mirrorOutput = False
def printErrors(self):
"Called by TestRunner after test run"
@@ -44,6 +57,13 @@ class TestResult(object):
def startTest(self, test):
"Called when the given test is about to be run"
self.testsRun += 1
+ self._mirrorOutput = False
+ if self.buffer:
+ if self._stderr_buffer is None:
+ self._stderr_buffer = io.StringIO()
+ self._stdout_buffer = io.StringIO()
+ sys.stdout = self._stdout_buffer
+ sys.stderr = self._stderr_buffer
def startTestRun(self):
"""Called once before any tests are executed.
@@ -53,6 +73,26 @@ class TestResult(object):
def stopTest(self, test):
"""Called when the given test has been run"""
+ if self.buffer:
+ if self._mirrorOutput:
+ output = sys.stdout.getvalue()
+ error = sys.stderr.getvalue()
+ if output:
+ if not output.endswith('\n'):
+ output += '\n'
+ self._original_stdout.write(STDOUT_LINE % output)
+ if error:
+ if not error.endswith('\n'):
+ error += '\n'
+ self._original_stderr.write(STDERR_LINE % error)
+
+ sys.stdout = self._original_stdout
+ sys.stderr = self._original_stderr
+ self._stdout_buffer.seek(0)
+ self._stdout_buffer.truncate()
+ self._stderr_buffer.seek(0)
+ self._stderr_buffer.truncate()
+ self._mirrorOutput = False
def stopTestRun(self):
"""Called once after all tests are executed.
@@ -66,12 +106,14 @@ class TestResult(object):
returned by sys.exc_info().
"""
self.errors.append((test, self._exc_info_to_string(err, test)))
+ self._mirrorOutput = True
@failfast
def addFailure(self, test, err):
"""Called when an error has occurred. 'err' is a tuple of values as
returned by sys.exc_info()."""
self.failures.append((test, self._exc_info_to_string(err, test)))
+ self._mirrorOutput = True
def addSuccess(self, test):
"Called when a test has completed successfully"
@@ -105,11 +147,29 @@ class TestResult(object):
# Skip test runner traceback levels
while tb and self._is_relevant_tb_level(tb):
tb = tb.tb_next
+
if exctype is test.failureException:
# Skip assert*() traceback levels
length = self._count_relevant_tb_levels(tb)
- return ''.join(traceback.format_exception(exctype, value, tb, length))
- return ''.join(traceback.format_exception(exctype, value, tb))
+ msgLines = traceback.format_exception(exctype, value, tb, length)
+ else:
+ chain = exctype is not None
+ msgLines = traceback.format_exception(exctype, value, tb,
+ chain=chain)
+
+ if self.buffer:
+ output = sys.stdout.getvalue()
+ error = sys.stderr.getvalue()
+ if output:
+ if not output.endswith('\n'):
+ output += '\n'
+ msgLines.append(STDOUT_LINE % output)
+ if error:
+ if not error.endswith('\n'):
+ error += '\n'
+ msgLines.append(STDERR_LINE % error)
+ return ''.join(msgLines)
+
def _is_relevant_tb_level(self, tb):
return '__unittest' in tb.tb_frame.f_globals
diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py
index 8065642..f13d623f 100644
--- a/Lib/unittest/runner.py
+++ b/Lib/unittest/runner.py
@@ -125,11 +125,12 @@ class TextTestRunner(object):
resultclass = TextTestResult
def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1,
- failfast=False, resultclass=None):
+ failfast=False, buffer=False, resultclass=None):
self.stream = _WritelnDecorator(stream)
self.descriptions = descriptions
self.verbosity = verbosity
self.failfast = failfast
+ self.buffer = buffer
if resultclass is not None:
self.resultclass = resultclass
@@ -141,6 +142,7 @@ class TextTestRunner(object):
result = self._makeResult()
registerResult(result)
result.failfast = self.failfast
+ result.buffer = self.buffer
startTime = time.time()
startTestRun = getattr(result, 'startTestRun', None)
if startTestRun is not None:
diff --git a/Lib/unittest/test/dummy.py b/Lib/unittest/test/dummy.py
new file mode 100644
index 0000000..e4f14e4
--- /dev/null
+++ b/Lib/unittest/test/dummy.py
@@ -0,0 +1 @@
+# Empty module for testing the loading of modules
diff --git a/Lib/unittest/test/test_assertions.py b/Lib/unittest/test/test_assertions.py
index fe65849..06b1ab8e5 100644
--- a/Lib/unittest/test/test_assertions.py
+++ b/Lib/unittest/test/test_assertions.py
@@ -1,3 +1,5 @@
+import datetime
+
import unittest
@@ -25,6 +27,28 @@ class Test_Assertions(unittest.TestCase):
self.assertRaises(self.failureException, self.assertNotAlmostEqual,
float('inf'), float('inf'))
+ def test_AmostEqualWithDelta(self):
+ self.assertAlmostEqual(1.1, 1.0, delta=0.5)
+ self.assertAlmostEqual(1.0, 1.1, delta=0.5)
+ self.assertNotAlmostEqual(1.1, 1.0, delta=0.05)
+ self.assertNotAlmostEqual(1.0, 1.1, delta=0.05)
+
+ self.assertRaises(self.failureException, self.assertAlmostEqual,
+ 1.1, 1.0, delta=0.05)
+ self.assertRaises(self.failureException, self.assertNotAlmostEqual,
+ 1.1, 1.0, delta=0.5)
+
+ self.assertRaises(TypeError, self.assertAlmostEqual,
+ 1.1, 1.0, places=2, delta=2)
+ self.assertRaises(TypeError, self.assertNotAlmostEqual,
+ 1.1, 1.0, places=2, delta=2)
+
+ first = datetime.datetime.now()
+ second = first + datetime.timedelta(seconds=10)
+ self.assertAlmostEqual(first, second,
+ delta=datetime.timedelta(seconds=20))
+ self.assertNotAlmostEqual(first, second,
+ delta=datetime.timedelta(seconds=5))
def test_assertRaises(self):
def _raise(e):
@@ -68,6 +92,16 @@ class Test_Assertions(unittest.TestCase):
else:
self.fail("assertRaises() didn't let exception pass through")
+ def testAssertNotRegexpMatches(self):
+ self.assertNotRegexpMatches('Ala ma kota', r'r+')
+ try:
+ self.assertNotRegexpMatches('Ala ma kota', r'k.t', 'Message')
+ except self.failureException as e:
+ self.assertIn("'kot'", e.args[0])
+ self.assertIn('Message', e.args[0])
+ else:
+ self.fail('assertNotRegexpMatches should have failed.')
+
class TestLongMessage(unittest.TestCase):
"""Test that the individual asserts honour longMessage.
diff --git a/Lib/unittest/test/test_break.py b/Lib/unittest/test/test_break.py
index a1a1eda..e3fae34 100644
--- a/Lib/unittest/test/test_break.py
+++ b/Lib/unittest/test/test_break.py
@@ -203,8 +203,9 @@ class TestBreak(unittest.TestCase):
p = Program(False)
p.runTests()
- self.assertEqual(FakeRunner.initArgs, [((), {'verbosity': verbosity,
- 'failfast': failfast})])
+ self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None,
+ 'verbosity': verbosity,
+ 'failfast': failfast})])
self.assertEqual(FakeRunner.runArgs, [test])
self.assertEqual(p.result, result)
@@ -215,8 +216,9 @@ class TestBreak(unittest.TestCase):
p = Program(True)
p.runTests()
- self.assertEqual(FakeRunner.initArgs, [((), {'verbosity': verbosity,
- 'failfast': failfast})])
+ self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None,
+ 'verbosity': verbosity,
+ 'failfast': failfast})])
self.assertEqual(FakeRunner.runArgs, [test])
self.assertEqual(p.result, result)
diff --git a/Lib/unittest/test/test_discovery.py b/Lib/unittest/test/test_discovery.py
index 5986271..1b0f08b 100644
--- a/Lib/unittest/test/test_discovery.py
+++ b/Lib/unittest/test/test_discovery.py
@@ -128,6 +128,7 @@ class TestDiscovery(unittest.TestCase):
loader = unittest.TestLoader()
original_isfile = os.path.isfile
+ original_isdir = os.path.isdir
def restore_isfile():
os.path.isfile = original_isfile
@@ -147,6 +148,12 @@ class TestDiscovery(unittest.TestCase):
self.assertIn(full_path, sys.path)
os.path.isfile = lambda path: True
+ os.path.isdir = lambda path: True
+
+ def restore_isdir():
+ os.path.isdir = original_isdir
+ self.addCleanup(restore_isdir)
+
_find_tests_args = []
def _find_tests(start_dir, pattern):
_find_tests_args.append((start_dir, pattern))
@@ -156,8 +163,8 @@ class TestDiscovery(unittest.TestCase):
suite = loader.discover('/foo/bar/baz', 'pattern', '/foo/bar')
- top_level_dir = os.path.abspath(os.path.normpath('/foo/bar'))
- start_dir = os.path.abspath(os.path.normpath('/foo/bar/baz'))
+ top_level_dir = os.path.abspath('/foo/bar')
+ start_dir = os.path.abspath('/foo/bar/baz')
self.assertEqual(suite, "['tests']")
self.assertEqual(loader._top_level_dir, top_level_dir)
self.assertEqual(_find_tests_args, [(start_dir, 'pattern')])
diff --git a/Lib/unittest/test/test_loader.py b/Lib/unittest/test/test_loader.py
index 373ace9..7def474 100644
--- a/Lib/unittest/test/test_loader.py
+++ b/Lib/unittest/test/test_loader.py
@@ -524,12 +524,8 @@ class Test_TestLoader(unittest.TestCase):
# We're going to try to load this module as a side-effect, so it
# better not be loaded before we try.
#
- # Why pick audioop? Google shows it isn't used very often, so there's
- # a good chance that it won't be imported when this test is run
- module_name = 'audioop'
-
- if module_name in sys.modules:
- del sys.modules[module_name]
+ module_name = 'unittest.test.dummy'
+ sys.modules.pop(module_name, None)
loader = unittest.TestLoader()
try:
@@ -538,7 +534,7 @@ class Test_TestLoader(unittest.TestCase):
self.assertIsInstance(suite, loader.suiteClass)
self.assertEqual(list(suite), [])
- # audioop should now be loaded, thanks to loadTestsFromName()
+ # module should now be loaded, thanks to loadTestsFromName()
self.assertIn(module_name, sys.modules)
finally:
if module_name in sys.modules:
@@ -911,12 +907,8 @@ class Test_TestLoader(unittest.TestCase):
# We're going to try to load this module as a side-effect, so it
# better not be loaded before we try.
#
- # Why pick audioop? Google shows it isn't used very often, so there's
- # a good chance that it won't be imported when this test is run
- module_name = 'audioop'
-
- if module_name in sys.modules:
- del sys.modules[module_name]
+ module_name = 'unittest.test.dummy'
+ sys.modules.pop(module_name, None)
loader = unittest.TestLoader()
try:
@@ -925,7 +917,7 @@ class Test_TestLoader(unittest.TestCase):
self.assertIsInstance(suite, loader.suiteClass)
self.assertEqual(list(suite), [unittest.TestSuite()])
- # audioop should now be loaded, thanks to loadTestsFromName()
+ # module should now be loaded, thanks to loadTestsFromName()
self.assertIn(module_name, sys.modules)
finally:
if module_name in sys.modules:
diff --git a/Lib/unittest/test/test_result.py b/Lib/unittest/test/test_result.py
index 967b662..c703032 100644
--- a/Lib/unittest/test/test_result.py
+++ b/Lib/unittest/test/test_result.py
@@ -1,6 +1,6 @@
import io
import sys
-import warnings
+import textwrap
from test import support
@@ -25,6 +25,8 @@ class Test_TestResult(unittest.TestCase):
self.assertEqual(len(result.failures), 0)
self.assertEqual(result.testsRun, 0)
self.assertEqual(result.shouldStop, False)
+ self.assertIsNone(result._stdout_buffer)
+ self.assertIsNone(result._stderr_buffer)
# "This method can be called to signal that the set of tests being
# run should be aborted by setting the TestResult's shouldStop
@@ -302,6 +304,8 @@ def __init__(self, stream=None, descriptions=None, verbosity=None):
self.errors = []
self.testsRun = 0
self.shouldStop = False
+ self.buffer = False
+
classDict['__init__'] = __init__
OldResult = type('OldResult', (object,), classDict)
@@ -355,3 +359,129 @@ class Test_OldTestResult(unittest.TestCase):
# This will raise an exception if TextTestRunner can't handle old
# test result objects
runner.run(Test('testFoo'))
+
+
+class TestOutputBuffering(unittest.TestCase):
+
+ def setUp(self):
+ self._real_out = sys.stdout
+ self._real_err = sys.stderr
+
+ def tearDown(self):
+ sys.stdout = self._real_out
+ sys.stderr = self._real_err
+
+ def testBufferOutputOff(self):
+ real_out = self._real_out
+ real_err = self._real_err
+
+ result = unittest.TestResult()
+ self.assertFalse(result.buffer)
+
+ self.assertIs(real_out, sys.stdout)
+ self.assertIs(real_err, sys.stderr)
+
+ result.startTest(self)
+
+ self.assertIs(real_out, sys.stdout)
+ self.assertIs(real_err, sys.stderr)
+
+ def testBufferOutputStartTestAddSuccess(self):
+ real_out = self._real_out
+ real_err = self._real_err
+
+ result = unittest.TestResult()
+ self.assertFalse(result.buffer)
+
+ result.buffer = True
+
+ self.assertIs(real_out, sys.stdout)
+ self.assertIs(real_err, sys.stderr)
+
+ result.startTest(self)
+
+ self.assertIsNot(real_out, sys.stdout)
+ self.assertIsNot(real_err, sys.stderr)
+ self.assertIsInstance(sys.stdout, io.StringIO)
+ self.assertIsInstance(sys.stderr, io.StringIO)
+ self.assertIsNot(sys.stdout, sys.stderr)
+
+ out_stream = sys.stdout
+ err_stream = sys.stderr
+
+ result._original_stdout = io.StringIO()
+ result._original_stderr = io.StringIO()
+
+ print('foo')
+ print('bar', file=sys.stderr)
+
+ self.assertEqual(out_stream.getvalue(), 'foo\n')
+ self.assertEqual(err_stream.getvalue(), 'bar\n')
+
+ self.assertEqual(result._original_stdout.getvalue(), '')
+ self.assertEqual(result._original_stderr.getvalue(), '')
+
+ result.addSuccess(self)
+ result.stopTest(self)
+
+ self.assertIs(sys.stdout, result._original_stdout)
+ self.assertIs(sys.stderr, result._original_stderr)
+
+ self.assertEqual(result._original_stdout.getvalue(), '')
+ self.assertEqual(result._original_stderr.getvalue(), '')
+
+ self.assertEqual(out_stream.getvalue(), '')
+ self.assertEqual(err_stream.getvalue(), '')
+
+
+ def getStartedResult(self):
+ result = unittest.TestResult()
+ result.buffer = True
+ result.startTest(self)
+ return result
+
+ def testBufferOutputAddErrorOrFailure(self):
+ for message_attr, add_attr, include_error in [
+ ('errors', 'addError', True),
+ ('failures', 'addFailure', False),
+ ('errors', 'addError', True),
+ ('failures', 'addFailure', False)
+ ]:
+ result = self.getStartedResult()
+ buffered_out = sys.stdout
+ buffered_err = sys.stderr
+ result._original_stdout = io.StringIO()
+ result._original_stderr = io.StringIO()
+
+ print('foo', file=sys.stdout)
+ if include_error:
+ print('bar', file=sys.stderr)
+
+
+ addFunction = getattr(result, add_attr)
+ addFunction(self, (None, None, None))
+ result.stopTest(self)
+
+ result_list = getattr(result, message_attr)
+ self.assertEqual(len(result_list), 1)
+
+ test, message = result_list[0]
+ expectedOutMessage = textwrap.dedent("""
+ Stdout:
+ foo
+ """)
+ expectedErrMessage = ''
+ if include_error:
+ expectedErrMessage = textwrap.dedent("""
+ Stderr:
+ bar
+ """)
+ expectedFullMessage = 'NoneType\n%s%s' % (expectedOutMessage, expectedErrMessage)
+
+ self.assertIs(test, self)
+ self.assertEqual(result._original_stdout.getvalue(), expectedOutMessage)
+ self.assertEqual(result._original_stderr.getvalue(), expectedErrMessage)
+ self.assertMultiLineEqual(message, expectedFullMessage)
+
+if __name__ == '__main__':
+ unittest.main()