Merged revisions 79464,79471,79623,79626,79630,79632,79643,79648-79649,79679,79685,79711,79761,79774,79777,79792-79794,79877,79898-79900 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r79464 | michael.foord | 2010-03-27 07:55:19 -0500 (Sat, 27 Mar 2010) | 1 line
A fix for running unittest tests on platforms without the audioop module (e.g. jython and IronPython)
........
r79471 | michael.foord | 2010-03-27 14:10:11 -0500 (Sat, 27 Mar 2010) | 4 lines
Addition of delta keyword argument to unittest.TestCase.assertAlmostEquals and assertNotAlmostEquals
This allows the comparison of objects by specifying a maximum difference; this includes the comparing of non-numeric objects that don't support rounding.
........
r79623 | michael.foord | 2010-04-02 16:42:47 -0500 (Fri, 02 Apr 2010) | 1 line
Addition of -b command line option to unittest for buffering stdout and stderr during test runs.
........
r79626 | michael.foord | 2010-04-02 17:08:29 -0500 (Fri, 02 Apr 2010) | 1 line
TestResult stores original sys.stdout and tests no longer use sys.__stdout__ (etc) in tests for unittest -b command line option
........
r79630 | michael.foord | 2010-04-02 17:30:56 -0500 (Fri, 02 Apr 2010) | 1 line
unittest tests no longer replace the sys.stdout put in place by regrtest
........
r79632 | michael.foord | 2010-04-02 17:55:59 -0500 (Fri, 02 Apr 2010) | 1 line
Issue #8038: Addition of unittest.TestCase.assertNotRegexpMatches
........
r79643 | michael.foord | 2010-04-02 20:15:21 -0500 (Fri, 02 Apr 2010) | 1 line
Support dotted module names for test discovery paths in unittest. Issue 8038.
........
r79648 | michael.foord | 2010-04-02 21:21:39 -0500 (Fri, 02 Apr 2010) | 1 line
Cross platform unittest.TestResult newline handling when buffering stdout / stderr.
........
r79649 | michael.foord | 2010-04-02 21:33:55 -0500 (Fri, 02 Apr 2010) | 1 line
Another attempt at a fix for unittest.test.test_result for windows line endings
........
r79679 | michael.foord | 2010-04-03 09:52:18 -0500 (Sat, 03 Apr 2010) | 1 line
Adding -b command line option to the unittest usage message.
........
r79685 | michael.foord | 2010-04-03 10:20:00 -0500 (Sat, 03 Apr 2010) | 1 line
Minor tweak to unittest command line usage message
........
r79711 | michael.foord | 2010-04-03 12:03:11 -0500 (Sat, 03 Apr 2010) | 1 line
Documenting new features in unittest
........
r79761 | michael.foord | 2010-04-04 17:41:54 -0500 (Sun, 04 Apr 2010) | 1 line
unittest documentation formatting changes
........
r79774 | michael.foord | 2010-04-04 18:28:44 -0500 (Sun, 04 Apr 2010) | 1 line
Adding documentation for new unittest.main() parameters
........
r79777 | michael.foord | 2010-04-04 19:39:50 -0500 (Sun, 04 Apr 2010) | 1 line
Document signal handling functions in unittest.rst
........
r79792 | michael.foord | 2010-04-05 05:26:26 -0500 (Mon, 05 Apr 2010) | 1 line
Documentation fixes for unittest
........
r79793 | michael.foord | 2010-04-05 05:28:27 -0500 (Mon, 05 Apr 2010) | 1 line
Furterh documentation fix for unittest.rst
........
r79794 | michael.foord | 2010-04-05 05:30:14 -0500 (Mon, 05 Apr 2010) | 1 line
Further documentation fix for unittest.rst
........
r79877 | michael.foord | 2010-04-06 18:18:16 -0500 (Tue, 06 Apr 2010) | 1 line
Fix module directory finding logic for dotted paths in unittest test discovery.
........
r79898 | michael.foord | 2010-04-07 18:04:22 -0500 (Wed, 07 Apr 2010) | 1 line
unittest.result.TestResult does not create its buffers until they're used. It uses StringIO not cStringIO. Issue 8333.
........
r79899 | michael.foord | 2010-04-07 19:04:24 -0500 (Wed, 07 Apr 2010) | 1 line
Switch regrtest to use StringIO instead of cStringIO for test_multiprocessing on Windows. Issue 8333.
........
r79900 | michael.foord | 2010-04-07 23:33:20 -0500 (Wed, 07 Apr 2010) | 1 line
Correction of unittest documentation typos and omissions
........
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 @@
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 @@
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 @@
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 @@
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 @@
__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 @@
-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 @@
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 @@
-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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
# 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..f13d623 100644
--- a/Lib/unittest/runner.py
+++ b/Lib/unittest/runner.py
@@ -125,11 +125,12 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
# 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 @@
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 @@
# 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 @@
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 @@
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 @@
self.errors = []
self.testsRun = 0
self.shouldStop = False
+ self.buffer = False
+
classDict['__init__'] = __init__
OldResult = type('OldResult', (object,), classDict)
@@ -355,3 +359,129 @@
# 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()