Kaydet (Commit) b48af54f authored tarafından Benjamin Peterson's avatar Benjamin Peterson

Merged revisions…

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
........
üst fc3c9cd7
This diff is collapsed.
...@@ -880,6 +880,10 @@ def runtest_inner(test, verbose, quiet, ...@@ -880,6 +880,10 @@ def runtest_inner(test, verbose, quiet,
testdir=None, huntrleaks=False, debug=False): testdir=None, huntrleaks=False, debug=False):
support.unload(test) support.unload(test)
testdir = findtestdir(testdir) testdir = findtestdir(testdir)
if verbose:
capture_stdout = None
else:
capture_stdout = io.StringIO()
test_time = 0.0 test_time = 0.0
refleak = False # True if the test leaked references. refleak = False # True if the test leaked references.
......
...@@ -12,6 +12,8 @@ import argparse ...@@ -12,6 +12,8 @@ import argparse
from io import StringIO from io import StringIO
from test import support from test import support
class StdIOBuffer(StringIO):
pass
class TestCase(unittest.TestCase): class TestCase(unittest.TestCase):
...@@ -25,6 +27,7 @@ class TestCase(unittest.TestCase): ...@@ -25,6 +27,7 @@ class TestCase(unittest.TestCase):
super(TestCase, self).assertEqual(obj1, obj2) super(TestCase, self).assertEqual(obj1, obj2)
class TempDirMixin(object): class TempDirMixin(object):
def setUp(self): def setUp(self):
...@@ -81,15 +84,15 @@ def stderr_to_parser_error(parse_args, *args, **kwargs): ...@@ -81,15 +84,15 @@ def stderr_to_parser_error(parse_args, *args, **kwargs):
# if this is being called recursively and stderr or stdout is already being # if this is being called recursively and stderr or stdout is already being
# redirected, simply call the function and let the enclosing function # redirected, simply call the function and let the enclosing function
# catch the exception # catch the exception
if isinstance(sys.stderr, StringIO) or isinstance(sys.stdout, StringIO): if isinstance(sys.stderr, StdIOBuffer) or isinstance(sys.stdout, StdIOBuffer):
return parse_args(*args, **kwargs) return parse_args(*args, **kwargs)
# if this is not being called recursively, redirect stderr and # if this is not being called recursively, redirect stderr and
# use it as the ArgumentParserError message # use it as the ArgumentParserError message
old_stdout = sys.stdout old_stdout = sys.stdout
old_stderr = sys.stderr old_stderr = sys.stderr
sys.stdout = StringIO() sys.stdout = StdIOBuffer()
sys.stderr = StringIO() sys.stderr = StdIOBuffer()
try: try:
try: try:
result = parse_args(*args, **kwargs) result = parse_args(*args, **kwargs)
...@@ -2634,7 +2637,7 @@ class TestHelpFormattingMetaclass(type): ...@@ -2634,7 +2637,7 @@ class TestHelpFormattingMetaclass(type):
parser = self._get_parser(tester) parser = self._get_parser(tester)
print_ = getattr(parser, 'print_%s' % self.func_suffix) print_ = getattr(parser, 'print_%s' % self.func_suffix)
old_stream = getattr(sys, self.std_name) old_stream = getattr(sys, self.std_name)
setattr(sys, self.std_name, StringIO()) setattr(sys, self.std_name, StdIOBuffer())
try: try:
print_() print_()
parser_text = getattr(sys, self.std_name).getvalue() parser_text = getattr(sys, self.std_name).getvalue()
...@@ -2645,7 +2648,7 @@ class TestHelpFormattingMetaclass(type): ...@@ -2645,7 +2648,7 @@ class TestHelpFormattingMetaclass(type):
def test_print_file(self, tester): def test_print_file(self, tester):
parser = self._get_parser(tester) parser = self._get_parser(tester)
print_ = getattr(parser, 'print_%s' % self.func_suffix) print_ = getattr(parser, 'print_%s' % self.func_suffix)
sfile = StringIO() sfile = StdIOBuffer()
print_(sfile) print_(sfile)
parser_text = sfile.getvalue() parser_text = sfile.getvalue()
self._test(tester, parser_text) self._test(tester, parser_text)
......
...@@ -502,10 +502,12 @@ class TestCase(object): ...@@ -502,10 +502,12 @@ class TestCase(object):
safe_repr(second))) safe_repr(second)))
raise self.failureException(msg) 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 """Fail if the two objects are unequal as determined by their
difference rounded to the given number of decimal places 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 Note that decimal places (from zero) are usually not the same
as significant digits (measured from the most signficant digit). as significant digits (measured from the most signficant digit).
...@@ -514,31 +516,62 @@ class TestCase(object): ...@@ -514,31 +516,62 @@ class TestCase(object):
compare almost equal. compare almost equal.
""" """
if first == second: if first == second:
# shortcut for inf # shortcut
return 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), standardMsg = '%s != %s within %r places' % (safe_repr(first),
safe_repr(second), safe_repr(second),
places) places)
msg = self._formatMessage(msg, standardMsg) msg = self._formatMessage(msg, standardMsg)
raise self.failureException(msg) 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 """Fail if the two objects are equal as determined by their
difference rounded to the given number of decimal places 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 Note that decimal places (from zero) are usually not the same
as significant digits (measured from the most signficant digit). as significant digits (measured from the most signficant digit).
Objects that are equal automatically fail. 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), standardMsg = '%s == %s within %r places' % (safe_repr(first),
safe_repr(second), safe_repr(second),
places) places)
msg = self._formatMessage(msg, standardMsg)
raise self.failureException(msg) msg = self._formatMessage(msg, standardMsg)
raise self.failureException(msg)
# Synonyms for assertion methods # Synonyms for assertion methods
...@@ -967,6 +1000,18 @@ class TestCase(object): ...@@ -967,6 +1000,18 @@ class TestCase(object):
msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text) msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text)
raise self.failureException(msg) 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): class FunctionTestCase(TestCase):
"""A test case that wraps a test function. """A test case that wraps a test function.
......
...@@ -166,27 +166,58 @@ class TestLoader(object): ...@@ -166,27 +166,58 @@ class TestLoader(object):
packages can continue discovery themselves. top_level_dir is stored so packages can continue discovery themselves. top_level_dir is stored so
load_tests does not need to pass this argument in to loader.discover(). 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: 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 # make top_level_dir optional if called from load_tests in a package
top_level_dir = self._top_level_dir top_level_dir = self._top_level_dir
elif top_level_dir is None: elif top_level_dir is None:
set_implicit_top = True
top_level_dir = start_dir top_level_dir = start_dir
top_level_dir = os.path.abspath(os.path.normpath(top_level_dir)) top_level_dir = os.path.abspath(top_level_dir)
start_dir = os.path.abspath(os.path.normpath(start_dir))
if not top_level_dir in sys.path: if not top_level_dir in sys.path:
# all test modules must be importable from the top level directory # all test modules must be importable from the top level directory
sys.path.append(top_level_dir) sys.path.append(top_level_dir)
self._top_level_dir = 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')): is_not_importable = False
# what about __init__.pyc or pyo (etc) 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) raise ImportError('Start directory is not importable: %r' % start_dir)
tests = list(self._find_tests(start_dir, pattern)) tests = list(self._find_tests(start_dir, pattern))
return self.suiteClass(tests) 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): def _get_name_from_path(self, path):
path = os.path.splitext(os.path.normpath(path))[0] path = os.path.splitext(os.path.normpath(path))[0]
......
...@@ -9,9 +9,9 @@ from .signals import installHandler ...@@ -9,9 +9,9 @@ from .signals import installHandler
__unittest = True __unittest = True
FAILFAST = " -f, --failfast Stop on first failure\n"
FAILFAST = " -f, --failfast Stop on first failure\n" CATCHBREAK = " -c, --catch Catch control-C and display results\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_AS_MAIN = """\
Usage: %(progName)s [options] [tests] Usage: %(progName)s [options] [tests]
...@@ -20,7 +20,7 @@ Options: ...@@ -20,7 +20,7 @@ Options:
-h, --help Show this message -h, --help Show this message
-v, --verbose Verbose output -v, --verbose Verbose output
-q, --quiet Minimal output -q, --quiet Minimal output
%(failfast)s%(catchbreak)s %(failfast)s%(catchbreak)s%(buffer)s
Examples: Examples:
%(progName)s test_module - run tests from test_module %(progName)s test_module - run tests from test_module
%(progName)s test_module.TestClass - run tests from %(progName)s test_module.TestClass - run tests from
...@@ -34,7 +34,7 @@ Alternative Usage: %(progName)s discover [options] ...@@ -34,7 +34,7 @@ Alternative Usage: %(progName)s discover [options]
Options: Options:
-v, --verbose Verbose output -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) -p pattern Pattern to match test files ('test*.py' default)
-t directory Top level directory of project (default to -t directory Top level directory of project (default to
start directory) start directory)
...@@ -50,7 +50,7 @@ Options: ...@@ -50,7 +50,7 @@ Options:
-h, --help Show this message -h, --help Show this message
-v, --verbose Verbose output -v, --verbose Verbose output
-q, --quiet Minimal output -q, --quiet Minimal output
%(failfast)s%(catchbreak)s %(failfast)s%(catchbreak)s%(buffer)s
Examples: Examples:
%(progName)s - run default set of tests %(progName)s - run default set of tests
%(progName)s MyTestSuite - run suite 'MyTestSuite' %(progName)s MyTestSuite - run suite 'MyTestSuite'
...@@ -68,12 +68,12 @@ class TestProgram(object): ...@@ -68,12 +68,12 @@ class TestProgram(object):
USAGE = USAGE_FROM_MODULE USAGE = USAGE_FROM_MODULE
# defaults for testing # defaults for testing
failfast = catchbreak = None failfast = catchbreak = buffer = None
def __init__(self, module='__main__', defaultTest=None, def __init__(self, module='__main__', defaultTest=None,
argv=None, testRunner=None, argv=None, testRunner=None,
testLoader=loader.defaultTestLoader, exit=True, testLoader=loader.defaultTestLoader, exit=True,
verbosity=1, failfast=None, catchbreak=None): verbosity=1, failfast=None, catchbreak=None, buffer=None):
if isinstance(module, str): if isinstance(module, str):
self.module = __import__(module) self.module = __import__(module)
for part in module.split('.')[1:]: for part in module.split('.')[1:]:
...@@ -87,6 +87,7 @@ class TestProgram(object): ...@@ -87,6 +87,7 @@ class TestProgram(object):
self.failfast = failfast self.failfast = failfast
self.catchbreak = catchbreak self.catchbreak = catchbreak
self.verbosity = verbosity self.verbosity = verbosity
self.buffer = buffer
self.defaultTest = defaultTest self.defaultTest = defaultTest
self.testRunner = testRunner self.testRunner = testRunner
self.testLoader = testLoader self.testLoader = testLoader
...@@ -97,11 +98,14 @@ class TestProgram(object): ...@@ -97,11 +98,14 @@ class TestProgram(object):
def usageExit(self, msg=None): def usageExit(self, msg=None):
if msg: if msg:
print(msg) print(msg)
usage = {'progName': self.progName, 'catchbreak': '', 'failfast': ''} usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '',
'buffer': ''}
if self.failfast != False: if self.failfast != False:
usage['failfast'] = FAILFAST usage['failfast'] = FAILFAST
if self.catchbreak != False: if self.catchbreak != False:
usage['catchbreak'] = CATCHBREAK usage['catchbreak'] = CATCHBREAK
if self.buffer != False:
usage['buffer'] = BUFFEROUTPUT
print(self.USAGE % usage) print(self.USAGE % usage)
sys.exit(2) sys.exit(2)
...@@ -111,9 +115,9 @@ class TestProgram(object): ...@@ -111,9 +115,9 @@ class TestProgram(object):
return return
import getopt import getopt
long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch'] long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer']
try: try:
options, args = getopt.getopt(argv[1:], 'hHvqfc', long_opts) options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts)
for opt, value in options: for opt, value in options:
if opt in ('-h','-H','--help'): if opt in ('-h','-H','--help'):
self.usageExit() self.usageExit()
...@@ -129,6 +133,10 @@ class TestProgram(object): ...@@ -129,6 +133,10 @@ class TestProgram(object):
if self.catchbreak is None: if self.catchbreak is None:
self.catchbreak = True self.catchbreak = True
# Should this raise an exception if -c is not valid? # 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: if len(args) == 0 and self.defaultTest is None:
# createTests will load tests from self.module # createTests will load tests from self.module
self.testNames = None self.testNames = None
...@@ -164,6 +172,10 @@ class TestProgram(object): ...@@ -164,6 +172,10 @@ class TestProgram(object):
parser.add_option('-c', '--catch', dest='catchbreak', default=False, parser.add_option('-c', '--catch', dest='catchbreak', default=False,
help='Catch ctrl-C and display results so far', help='Catch ctrl-C and display results so far',
action='store_true') 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='.', parser.add_option('-s', '--start-directory', dest='start', default='.',
help="Directory to start discovery ('.' default)") help="Directory to start discovery ('.' default)")
parser.add_option('-p', '--pattern', dest='pattern', default='test*.py', parser.add_option('-p', '--pattern', dest='pattern', default='test*.py',
...@@ -184,6 +196,8 @@ class TestProgram(object): ...@@ -184,6 +196,8 @@ class TestProgram(object):
self.failfast = options.failfast self.failfast = options.failfast
if self.catchbreak is None: if self.catchbreak is None:
self.catchbreak = options.catchbreak self.catchbreak = options.catchbreak
if self.buffer is None:
self.buffer = options.buffer
if options.verbose: if options.verbose:
self.verbosity = 2 self.verbosity = 2
...@@ -203,9 +217,10 @@ class TestProgram(object): ...@@ -203,9 +217,10 @@ class TestProgram(object):
if isinstance(self.testRunner, type): if isinstance(self.testRunner, type):
try: try:
testRunner = self.testRunner(verbosity=self.verbosity, testRunner = self.testRunner(verbosity=self.verbosity,
failfast=self.failfast) failfast=self.failfast,
buffer=self.buffer)
except TypeError: except TypeError:
# didn't accept the verbosity or failfast arguments # didn't accept the verbosity, buffer or failfast arguments
testRunner = self.testRunner() testRunner = self.testRunner()
else: else:
# it is assumed to be a TestRunner instance # it is assumed to be a TestRunner instance
......
"""Test result object""" """Test result object"""
import os
import io
import sys
import traceback import traceback
from . import util from . import util
...@@ -15,6 +18,10 @@ def failfast(method): ...@@ -15,6 +18,10 @@ def failfast(method):
return method(self, *args, **kw) return method(self, *args, **kw)
return inner return inner
STDOUT_LINE = '\nStdout:\n%s'
STDERR_LINE = '\nStderr:\n%s'
class TestResult(object): class TestResult(object):
"""Holder for test result information. """Holder for test result information.
...@@ -37,6 +44,12 @@ class TestResult(object): ...@@ -37,6 +44,12 @@ class TestResult(object):
self.expectedFailures = [] self.expectedFailures = []
self.unexpectedSuccesses = [] self.unexpectedSuccesses = []
self.shouldStop = False 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): def printErrors(self):
"Called by TestRunner after test run" "Called by TestRunner after test run"
...@@ -44,6 +57,13 @@ class TestResult(object): ...@@ -44,6 +57,13 @@ class TestResult(object):
def startTest(self, test): def startTest(self, test):
"Called when the given test is about to be run" "Called when the given test is about to be run"
self.testsRun += 1 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): def startTestRun(self):
"""Called once before any tests are executed. """Called once before any tests are executed.
...@@ -53,6 +73,26 @@ class TestResult(object): ...@@ -53,6 +73,26 @@ class TestResult(object):
def stopTest(self, test): def stopTest(self, test):
"""Called when the given test has been run""" """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): def stopTestRun(self):
"""Called once after all tests are executed. """Called once after all tests are executed.
...@@ -66,12 +106,14 @@ class TestResult(object): ...@@ -66,12 +106,14 @@ class TestResult(object):
returned by sys.exc_info(). returned by sys.exc_info().
""" """
self.errors.append((test, self._exc_info_to_string(err, test))) self.errors.append((test, self._exc_info_to_string(err, test)))
self._mirrorOutput = True
@failfast @failfast
def addFailure(self, test, err): def addFailure(self, test, err):
"""Called when an error has occurred. 'err' is a tuple of values as """Called when an error has occurred. 'err' is a tuple of values as
returned by sys.exc_info().""" returned by sys.exc_info()."""
self.failures.append((test, self._exc_info_to_string(err, test))) self.failures.append((test, self._exc_info_to_string(err, test)))
self._mirrorOutput = True
def addSuccess(self, test): def addSuccess(self, test):
"Called when a test has completed successfully" "Called when a test has completed successfully"
...@@ -105,11 +147,29 @@ class TestResult(object): ...@@ -105,11 +147,29 @@ class TestResult(object):
# Skip test runner traceback levels # Skip test runner traceback levels
while tb and self._is_relevant_tb_level(tb): while tb and self._is_relevant_tb_level(tb):
tb = tb.tb_next tb = tb.tb_next
if exctype is test.failureException: if exctype is test.failureException:
# Skip assert*() traceback levels # Skip assert*() traceback levels
length = self._count_relevant_tb_levels(tb) length = self._count_relevant_tb_levels(tb)
return ''.join(traceback.format_exception(exctype, value, tb, length)) msgLines = traceback.format_exception(exctype, value, tb, length)
return ''.join(traceback.format_exception(exctype, value, tb)) 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): def _is_relevant_tb_level(self, tb):
return '__unittest' in tb.tb_frame.f_globals return '__unittest' in tb.tb_frame.f_globals
......
...@@ -125,11 +125,12 @@ class TextTestRunner(object): ...@@ -125,11 +125,12 @@ class TextTestRunner(object):
resultclass = TextTestResult resultclass = TextTestResult
def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1, 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.stream = _WritelnDecorator(stream)
self.descriptions = descriptions self.descriptions = descriptions
self.verbosity = verbosity self.verbosity = verbosity
self.failfast = failfast self.failfast = failfast
self.buffer = buffer
if resultclass is not None: if resultclass is not None:
self.resultclass = resultclass self.resultclass = resultclass
...@@ -141,6 +142,7 @@ class TextTestRunner(object): ...@@ -141,6 +142,7 @@ class TextTestRunner(object):
result = self._makeResult() result = self._makeResult()
registerResult(result) registerResult(result)
result.failfast = self.failfast result.failfast = self.failfast
result.buffer = self.buffer
startTime = time.time() startTime = time.time()
startTestRun = getattr(result, 'startTestRun', None) startTestRun = getattr(result, 'startTestRun', None)
if startTestRun is not None: if startTestRun is not None:
......
# Empty module for testing the loading of modules
import datetime
import unittest import unittest
...@@ -25,6 +27,28 @@ class Test_Assertions(unittest.TestCase): ...@@ -25,6 +27,28 @@ class Test_Assertions(unittest.TestCase):
self.assertRaises(self.failureException, self.assertNotAlmostEqual, self.assertRaises(self.failureException, self.assertNotAlmostEqual,
float('inf'), float('inf')) 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 test_assertRaises(self):
def _raise(e): def _raise(e):
...@@ -68,6 +92,16 @@ class Test_Assertions(unittest.TestCase): ...@@ -68,6 +92,16 @@ class Test_Assertions(unittest.TestCase):
else: else:
self.fail("assertRaises() didn't let exception pass through") 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): class TestLongMessage(unittest.TestCase):
"""Test that the individual asserts honour longMessage. """Test that the individual asserts honour longMessage.
......
...@@ -203,8 +203,9 @@ class TestBreak(unittest.TestCase): ...@@ -203,8 +203,9 @@ class TestBreak(unittest.TestCase):
p = Program(False) p = Program(False)
p.runTests() p.runTests()
self.assertEqual(FakeRunner.initArgs, [((), {'verbosity': verbosity, self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None,
'failfast': failfast})]) 'verbosity': verbosity,
'failfast': failfast})])
self.assertEqual(FakeRunner.runArgs, [test]) self.assertEqual(FakeRunner.runArgs, [test])
self.assertEqual(p.result, result) self.assertEqual(p.result, result)
...@@ -215,8 +216,9 @@ class TestBreak(unittest.TestCase): ...@@ -215,8 +216,9 @@ class TestBreak(unittest.TestCase):
p = Program(True) p = Program(True)
p.runTests() p.runTests()
self.assertEqual(FakeRunner.initArgs, [((), {'verbosity': verbosity, self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None,
'failfast': failfast})]) 'verbosity': verbosity,
'failfast': failfast})])
self.assertEqual(FakeRunner.runArgs, [test]) self.assertEqual(FakeRunner.runArgs, [test])
self.assertEqual(p.result, result) self.assertEqual(p.result, result)
......
...@@ -128,6 +128,7 @@ class TestDiscovery(unittest.TestCase): ...@@ -128,6 +128,7 @@ class TestDiscovery(unittest.TestCase):
loader = unittest.TestLoader() loader = unittest.TestLoader()
original_isfile = os.path.isfile original_isfile = os.path.isfile
original_isdir = os.path.isdir
def restore_isfile(): def restore_isfile():
os.path.isfile = original_isfile os.path.isfile = original_isfile
...@@ -147,6 +148,12 @@ class TestDiscovery(unittest.TestCase): ...@@ -147,6 +148,12 @@ class TestDiscovery(unittest.TestCase):
self.assertIn(full_path, sys.path) self.assertIn(full_path, sys.path)
os.path.isfile = lambda path: True 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 = [] _find_tests_args = []
def _find_tests(start_dir, pattern): def _find_tests(start_dir, pattern):
_find_tests_args.append((start_dir, pattern)) _find_tests_args.append((start_dir, pattern))
...@@ -156,8 +163,8 @@ class TestDiscovery(unittest.TestCase): ...@@ -156,8 +163,8 @@ class TestDiscovery(unittest.TestCase):
suite = loader.discover('/foo/bar/baz', 'pattern', '/foo/bar') suite = loader.discover('/foo/bar/baz', 'pattern', '/foo/bar')
top_level_dir = os.path.abspath(os.path.normpath('/foo/bar')) top_level_dir = os.path.abspath('/foo/bar')
start_dir = os.path.abspath(os.path.normpath('/foo/bar/baz')) start_dir = os.path.abspath('/foo/bar/baz')
self.assertEqual(suite, "['tests']") self.assertEqual(suite, "['tests']")
self.assertEqual(loader._top_level_dir, top_level_dir) self.assertEqual(loader._top_level_dir, top_level_dir)
self.assertEqual(_find_tests_args, [(start_dir, 'pattern')]) self.assertEqual(_find_tests_args, [(start_dir, 'pattern')])
......
...@@ -524,12 +524,8 @@ class Test_TestLoader(unittest.TestCase): ...@@ -524,12 +524,8 @@ class Test_TestLoader(unittest.TestCase):
# We're going to try to load this module as a side-effect, so it # We're going to try to load this module as a side-effect, so it
# better not be loaded before we try. # better not be loaded before we try.
# #
# Why pick audioop? Google shows it isn't used very often, so there's module_name = 'unittest.test.dummy'
# a good chance that it won't be imported when this test is run sys.modules.pop(module_name, None)
module_name = 'audioop'
if module_name in sys.modules:
del sys.modules[module_name]
loader = unittest.TestLoader() loader = unittest.TestLoader()
try: try:
...@@ -538,7 +534,7 @@ class Test_TestLoader(unittest.TestCase): ...@@ -538,7 +534,7 @@ class Test_TestLoader(unittest.TestCase):
self.assertIsInstance(suite, loader.suiteClass) self.assertIsInstance(suite, loader.suiteClass)
self.assertEqual(list(suite), []) 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) self.assertIn(module_name, sys.modules)
finally: finally:
if module_name in sys.modules: if module_name in sys.modules:
...@@ -911,12 +907,8 @@ class Test_TestLoader(unittest.TestCase): ...@@ -911,12 +907,8 @@ class Test_TestLoader(unittest.TestCase):
# We're going to try to load this module as a side-effect, so it # We're going to try to load this module as a side-effect, so it
# better not be loaded before we try. # better not be loaded before we try.
# #
# Why pick audioop? Google shows it isn't used very often, so there's module_name = 'unittest.test.dummy'
# a good chance that it won't be imported when this test is run sys.modules.pop(module_name, None)
module_name = 'audioop'
if module_name in sys.modules:
del sys.modules[module_name]
loader = unittest.TestLoader() loader = unittest.TestLoader()
try: try:
...@@ -925,7 +917,7 @@ class Test_TestLoader(unittest.TestCase): ...@@ -925,7 +917,7 @@ class Test_TestLoader(unittest.TestCase):
self.assertIsInstance(suite, loader.suiteClass) self.assertIsInstance(suite, loader.suiteClass)
self.assertEqual(list(suite), [unittest.TestSuite()]) 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) self.assertIn(module_name, sys.modules)
finally: finally:
if module_name in sys.modules: if module_name in sys.modules:
......
import io import io
import sys import sys
import warnings import textwrap
from test import support from test import support
...@@ -25,6 +25,8 @@ class Test_TestResult(unittest.TestCase): ...@@ -25,6 +25,8 @@ class Test_TestResult(unittest.TestCase):
self.assertEqual(len(result.failures), 0) self.assertEqual(len(result.failures), 0)
self.assertEqual(result.testsRun, 0) self.assertEqual(result.testsRun, 0)
self.assertEqual(result.shouldStop, False) 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 # "This method can be called to signal that the set of tests being
# run should be aborted by setting the TestResult's shouldStop # run should be aborted by setting the TestResult's shouldStop
...@@ -302,6 +304,8 @@ def __init__(self, stream=None, descriptions=None, verbosity=None): ...@@ -302,6 +304,8 @@ def __init__(self, stream=None, descriptions=None, verbosity=None):
self.errors = [] self.errors = []
self.testsRun = 0 self.testsRun = 0
self.shouldStop = False self.shouldStop = False
self.buffer = False
classDict['__init__'] = __init__ classDict['__init__'] = __init__
OldResult = type('OldResult', (object,), classDict) OldResult = type('OldResult', (object,), classDict)
...@@ -355,3 +359,129 @@ class Test_OldTestResult(unittest.TestCase): ...@@ -355,3 +359,129 @@ class Test_OldTestResult(unittest.TestCase):
# This will raise an exception if TextTestRunner can't handle old # This will raise an exception if TextTestRunner can't handle old
# test result objects # test result objects
runner.run(Test('testFoo')) 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()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment