Merged revisions 78227,78229,78288,78348,78377,78770,78774-78776,78810 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r78227 | michael.foord | 2010-02-18 14:30:09 -0600 (Thu, 18 Feb 2010) | 1 line
unittest.TestCase uses safe_repr for producing failure messages. Partial fix for issue 7956
........
r78229 | michael.foord | 2010-02-18 15:37:07 -0600 (Thu, 18 Feb 2010) | 1 line
Fix unittest.TestCase.assertDictContainsSubset so it can't die with unicode issues when constructing failure messages. Issue 7956
........
r78288 | michael.foord | 2010-02-21 08:48:59 -0600 (Sun, 21 Feb 2010) | 1 line
Silence UnicodeWarning in crazy unittest test.
........
r78348 | michael.foord | 2010-02-22 17:28:32 -0600 (Mon, 22 Feb 2010) | 1 line
Support for old TestResult object (unittest) with warnings when using unsupported features.
........
r78377 | michael.foord | 2010-02-23 11:00:53 -0600 (Tue, 23 Feb 2010) | 1 line
unittest.TestResult can now be used with the TextTestRunner. TextTestRunner compatible with old TestResult objects.
........
r78770 | michael.foord | 2010-03-07 14:22:12 -0600 (Sun, 07 Mar 2010) | 1 line
Fix for potentials errors in constructing unittest failure messages. Plus skipped test methods no longer run setUp and tearDown (Issue 8059)
........
r78774 | michael.foord | 2010-03-07 16:04:55 -0600 (Sun, 07 Mar 2010) | 1 line
Addition of setUpClass and setUpModule shared fixtures to unittest.
........
r78775 | michael.foord | 2010-03-07 17:10:36 -0600 (Sun, 07 Mar 2010) | 1 line
Fix accidental name rebinding in unittest py3k warning filtering.
........
r78776 | michael.foord | 2010-03-07 17:16:20 -0600 (Sun, 07 Mar 2010) | 1 line
Remove accidental print statement from last commit.
........
r78810 | raymond.hettinger | 2010-03-09 02:44:18 -0600 (Tue, 09 Mar 2010) | 5 lines
Improve the basic example.
* Show both the decorator and regular form for assertRaises()
* Use assertTrue() instead of assertIn() to teach useful minimal subset of the API
........
diff --git a/Lib/unittest/suite.py b/Lib/unittest/suite.py
index 35b7d91..ddd83ae 100644
--- a/Lib/unittest/suite.py
+++ b/Lib/unittest/suite.py
@@ -1,17 +1,13 @@
"""TestSuite"""
+import sys
+
from . import case
from . import util
-class TestSuite(object):
- """A test suite is a composite test consisting of a number of TestCases.
-
- For use, create an instance of TestSuite, then add test case instances.
- When all tests have been added, the suite can be passed to a test
- runner, such as TextTestRunner. It will run the individual test cases
- in the order in which they were added, aggregating the results. When
- subclassing, do not forget to call the base class constructor.
+class BaseTestSuite(object):
+ """A simple test suite that doesn't provide class or module shared fixtures.
"""
def __init__(self, tests=()):
self._tests = []
@@ -67,3 +63,190 @@
"""Run the tests without collecting errors in a TestResult"""
for test in self:
test.debug()
+
+
+class TestSuite(BaseTestSuite):
+ """A test suite is a composite test consisting of a number of TestCases.
+
+ For use, create an instance of TestSuite, then add test case instances.
+ When all tests have been added, the suite can be passed to a test
+ runner, such as TextTestRunner. It will run the individual test cases
+ in the order in which they were added, aggregating the results. When
+ subclassing, do not forget to call the base class constructor.
+ """
+
+
+ def run(self, result):
+ self._wrapped_run(result)
+ self._tearDownPreviousClass(None, result)
+ self._handleModuleTearDown(result)
+ return result
+
+ ################################
+ # private methods
+ def _wrapped_run(self, result):
+ for test in self:
+ if result.shouldStop:
+ break
+
+ if _isnotsuite(test):
+ self._tearDownPreviousClass(test, result)
+ self._handleModuleFixture(test, result)
+ self._handleClassSetUp(test, result)
+ result._previousTestClass = test.__class__
+
+ if (getattr(test.__class__, '_classSetupFailed', False) or
+ getattr(result, '_moduleSetUpFailed', False)):
+ continue
+
+ if hasattr(test, '_wrapped_run'):
+ test._wrapped_run(result)
+ else:
+ test(result)
+
+ def _handleClassSetUp(self, test, result):
+ previousClass = getattr(result, '_previousTestClass', None)
+ currentClass = test.__class__
+ if currentClass == previousClass:
+ return
+ if result._moduleSetUpFailed:
+ return
+ if getattr(currentClass, "__unittest_skip__", False):
+ return
+
+ currentClass._classSetupFailed = False
+
+ setUpClass = getattr(currentClass, 'setUpClass', None)
+ if setUpClass is not None:
+ try:
+ setUpClass()
+ except:
+ currentClass._classSetupFailed = True
+ self._addClassSetUpError(result, currentClass)
+
+ def _get_previous_module(self, result):
+ previousModule = None
+ previousClass = getattr(result, '_previousTestClass', None)
+ if previousClass is not None:
+ previousModule = previousClass.__module__
+ return previousModule
+
+
+ def _handleModuleFixture(self, test, result):
+ previousModule = self._get_previous_module(result)
+ currentModule = test.__class__.__module__
+ if currentModule == previousModule:
+ return
+
+ self._handleModuleTearDown(result)
+
+
+ result._moduleSetUpFailed = False
+ try:
+ module = sys.modules[currentModule]
+ except KeyError:
+ return
+ setUpModule = getattr(module, 'setUpModule', None)
+ if setUpModule is not None:
+ try:
+ setUpModule()
+ except:
+ result._moduleSetUpFailed = True
+ error = _ErrorHolder('setUpModule (%s)' % currentModule)
+ result.addError(error, sys.exc_info())
+
+ def _handleModuleTearDown(self, result):
+ previousModule = self._get_previous_module(result)
+ if previousModule is None:
+ return
+ if result._moduleSetUpFailed:
+ return
+
+ try:
+ module = sys.modules[previousModule]
+ except KeyError:
+ return
+
+ tearDownModule = getattr(module, 'tearDownModule', None)
+ if tearDownModule is not None:
+ try:
+ tearDownModule()
+ except:
+ error = _ErrorHolder('tearDownModule (%s)' % previousModule)
+ result.addError(error, sys.exc_info())
+
+ def _tearDownPreviousClass(self, test, result):
+ previousClass = getattr(result, '_previousTestClass', None)
+ currentClass = test.__class__
+ if currentClass == previousClass:
+ return
+ if getattr(previousClass, '_classSetupFailed', False):
+ return
+ if getattr(result, '_moduleSetUpFailed', False):
+ return
+ if getattr(previousClass, "__unittest_skip__", False):
+ return
+
+ tearDownClass = getattr(previousClass, 'tearDownClass', None)
+ if tearDownClass is not None:
+ try:
+ tearDownClass()
+ except:
+ self._addClassTearDownError(result)
+
+ def _addClassTearDownError(self, result):
+ className = util.strclass(result._previousTestClass)
+ error = _ErrorHolder('classTearDown (%s)' % className)
+ result.addError(error, sys.exc_info())
+
+ def _addClassSetUpError(self, result, klass):
+ className = util.strclass(klass)
+ error = _ErrorHolder('classSetUp (%s)' % className)
+ result.addError(error, sys.exc_info())
+
+
+class _ErrorHolder(object):
+ """
+ Placeholder for a TestCase inside a result. As far as a TestResult
+ is concerned, this looks exactly like a unit test. Used to insert
+ arbitrary errors into a test suite run.
+ """
+ # Inspired by the ErrorHolder from Twisted:
+ # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
+
+ # attribute used by TestResult._exc_info_to_string
+ failureException = None
+
+ def __init__(self, description):
+ self.description = description
+
+ def id(self):
+ return self.description
+
+ def shortDescription(self):
+ return None
+
+ def __repr__(self):
+ return "<ErrorHolder description=%r>" % (self.description,)
+
+ def __str__(self):
+ return self.id()
+
+ def run(self, result):
+ # could call result.addError(...) - but this test-like object
+ # shouldn't be run anyway
+ pass
+
+ def __call__(self, result):
+ return self.run(result)
+
+ def countTestCases(self):
+ return 0
+
+def _isnotsuite(test):
+ "A crude way to tell apart testcases and suites with duck-typing"
+ try:
+ iter(test)
+ except TypeError:
+ return True
+ return False