diff options
author | jpellerin <devnull@localhost> | 2009-12-02 17:51:50 -0500 |
---|---|---|
committer | jpellerin <devnull@localhost> | 2009-12-02 17:51:50 -0500 |
commit | e2df9e6b038d8e45dd7f9623618d6dfcb38313a5 (patch) | |
tree | e690d700b4eea086c8b5a281d7dcc64e0477a863 | |
parent | 7c8482f9a40face2a343c5840ef0e21ea718c0bd (diff) | |
download | nose-e2df9e6b038d8e45dd7f9623618d6dfcb38313a5.tar.gz |
Improved compatibility with 2.7 builtin skip
-rw-r--r-- | nose/plugins/errorclass.py | 26 | ||||
-rw-r--r-- | nose/plugins/skip.py | 11 | ||||
-rw-r--r-- | nose/proxy.py | 28 | ||||
-rw-r--r-- | nose/result.py | 53 | ||||
-rw-r--r-- | unit_tests/test_skip_plugin.py | 13 |
5 files changed, 90 insertions, 41 deletions
diff --git a/nose/plugins/errorclass.py b/nose/plugins/errorclass.py index 51b20ed..d327194 100644 --- a/nose/plugins/errorclass.py +++ b/nose/plugins/errorclass.py @@ -29,7 +29,7 @@ into the tuples used by the error handling and reporting functions in the result. This is an internal format and subject to change; you should always use the declarative syntax for attaching ErrorClasses to an ErrorClass plugin. - + >>> TodoError.errorClasses # doctest: +ELLIPSIS ((<class ...Todo...>, ('todo', 'TODO', True)),) @@ -42,7 +42,7 @@ Let's see the plugin in action. First some boilerplate. ... from unittest.runner import _WritelnDecorator ... except ImportError: ... from unittest import _WritelnDecorator - ... + ... >>> buf = _WritelnDecorator(sys.stdout) Now define a test case that raises a Todo. @@ -154,6 +154,7 @@ class ErrorClassPlugin(Plugin): result.errorClasses[cls] = (storage, label, isfail) def patchResult(self, result): + result.printLabel = print_label_patch(result) result._orig_addError, result.addError = \ result.addError, add_error_patch(result) result._orig_wasSuccessful, result.wasSuccessful = \ @@ -161,6 +162,9 @@ class ErrorClassPlugin(Plugin): if hasattr(result, 'printErrors'): result._orig_printErrors, result.printErrors = \ result.printErrors, print_errors_patch(result) + if hasattr(result, 'addSkip'): + result._orig_addSkip, result.addSkip = \ + result.addSkip, add_skip_patch(result) result.errorClasses = {} @@ -181,6 +185,14 @@ def print_errors_patch(result): TextTestResult.printErrors.im_func, result, result.__class__) +def print_label_patch(result): + """Create a new printLabel method that prints errorClasses items + as well. + """ + return instancemethod( + TextTestResult.printLabel.im_func, result, result.__class__) + + def wassuccessful_patch(result): """Create a new wasSuccessful method that checks errorClasses for exceptions that were put into other slots than error or failure @@ -189,7 +201,15 @@ def wassuccessful_patch(result): return instancemethod( TextTestResult.wasSuccessful.im_func, result, result.__class__) - + +def add_skip_patch(result): + """Create a new addSkip method to patch into a result instance + that delegates to addError. + """ + return instancemethod( + TextTestResult.addSkip.im_func, result, result.__class__) + + if __name__ == '__main__': import doctest doctest.testmod() diff --git a/nose/plugins/skip.py b/nose/plugins/skip.py index 3b2ff1c..27e5162 100644 --- a/nose/plugins/skip.py +++ b/nose/plugins/skip.py @@ -9,9 +9,14 @@ is enabled by default but may be disabled with the ``--no-skip`` option. from nose.plugins.errorclass import ErrorClass, ErrorClassPlugin -class SkipTest(Exception): - """Raise this exception to mark a test as skipped. - """ +try: + # 2.7 + from unittest.case import SkipTest +except ImportError: + # 2.6 and below + class SkipTest(Exception): + """Raise this exception to mark a test as skipped. + """ pass diff --git a/nose/proxy.py b/nose/proxy.py index c2c99cb..ba5b638 100644 --- a/nose/proxy.py +++ b/nose/proxy.py @@ -72,8 +72,8 @@ class ResultProxy(object): the wrapped test case) as each result call is made. Finally, the real result method is called, also with the nose.case.Test instance as the test parameter. - - """ + + """ def __init__(self, result, test, config=None): if config is None: config = Config() @@ -90,12 +90,12 @@ class ResultProxy(object): # .test's .test. or my .test.test's .case case = getattr(self.test, 'test', None) - assert (test is self.test - or test is case - or test is getattr(case, '_nose_case', None)), ( - "ResultProxy for %r (%s) was called with test %r (%s)" + assert (test is self.test + or test is case + or test is getattr(case, '_nose_case', None)), ( + "ResultProxy for %r (%s) was called with test %r (%s)" % (self.test, id(self.test), test, id(test))) - + def afterTest(self, test): self.assertMyTest(test) self.plugins.afterTest(self.test) @@ -137,7 +137,15 @@ class ResultProxy(object): self.result.addFailure(self.test, err) if self.config.stopOnError: self.shouldStop = True - + + def addSkip(self, test, reason): + # 2.7 compat shim + from nose.plugins.skip import SkipTest + self.assertMyTest(test) + plugins = self.plugins + plugins.addError(self.test, (SkipTest, reason, None)) + self.result.addSkip(self.test, reason) + def addSuccess(self, test): self.assertMyTest(test) self.plugins.addSuccess(self.test) @@ -147,10 +155,10 @@ class ResultProxy(object): self.assertMyTest(test) self.plugins.startTest(self.test) self.result.startTest(self.test) - + def stop(self): self.result.stop() - + def stopTest(self, test): self.assertMyTest(test) self.plugins.stopTest(self.test) diff --git a/nose/result.py b/nose/result.py index da1369d..9fd90d6 100644 --- a/nose/result.py +++ b/nose/result.py @@ -32,23 +32,30 @@ class TextTestResult(_TextTestResult): """Text test result that extends unittest's default test result support for a configurable set of errorClasses (eg, Skip, Deprecated, TODO) that extend the errors/failures/success triad. - """ + """ def __init__(self, stream, descriptions, verbosity, config=None, - errorClasses=None): + errorClasses=None): if errorClasses is None: errorClasses = {} self.errorClasses = errorClasses if config is None: - config = Config() + config = Config() self.config = config _TextTestResult.__init__(self, stream, descriptions, verbosity) - + + def addSkip(self, test, reason): + # 2.7 skip compat + from nose.plugins.skip import SkipTest + if SkipTest in self.errorClasses: + storage, label, isfail = self.errorClasses[SkipTest] + storage.append((test, reason)) + self.printLabel(label, (SkipTest, reason, None)) + def addError(self, test, err): """Overrides normal addError to add support for errorClasses. If the exception is a registered class, the error will be added to the list for that class, not errors. """ - stream = getattr(self, 'stream', None) ec, ev, tb = err try: exc_info = self._exc_info_to_string(err, test) @@ -56,28 +63,32 @@ class TextTestResult(_TextTestResult): # 2.3 compat exc_info = self._exc_info_to_string(err) for cls, (storage, label, isfail) in self.errorClasses.items(): + #if 'Skip' in cls.__name__ or 'Skip' in ec.__name__: + # from nose.tools import set_trace + # set_trace() if isclass(ec) and issubclass(ec, cls): if isfail: test.passed = False storage.append((test, exc_info)) - # Might get patched into a streamless result - if stream is not None: - if self.showAll: - message = [label] - detail = _exception_detail(err[1]) - if detail: - message.append(detail) - stream.writeln(": ".join(message)) - elif self.dots: - stream.write(label[:1]) + self.printLabel(label, err) return self.errors.append((test, exc_info)) test.passed = False + self.printLabel('ERROR') + + def printLabel(self, label, err=None): + # Might get patched into a streamless result + stream = getattr(self, 'stream', None) if stream is not None: if self.showAll: - self.stream.writeln('ERROR') + message = [label] + if err: + detail = _exception_detail(err[1]) + if detail: + message.append(detail) + stream.writeln(": ".join(message)) elif self.dots: - stream.write('E') + stream.write(label[:1]) def printErrors(self): """Overrides to print all errorClasses errors as well. @@ -100,7 +111,7 @@ class TextTestResult(_TextTestResult): taken = float(stop - start) run = self.testsRun plural = run != 1 and "s" or "" - + writeln(self.separator2) writeln("Ran %s test%s in %.3fs" % (run, plural, taken)) writeln() @@ -161,6 +172,10 @@ class TextTestResult(_TextTestResult): self.stream.write('E') def _exc_info_to_string(self, err, test=None): + # 2.7 skip compat + from nose.plugins.skip import SkipTest + if issubclass(err[0], SkipTest): + return str(err[1]) # 2.3/2.4 -- 2.4 passes test, 2.3 does not try: return _TextTestResult._exc_info_to_string(self, err, test) @@ -175,5 +190,5 @@ def ln(*arg, **kw): "from nose.result in a future release. Please update your imports ", DeprecationWarning) return _ln(*arg, **kw) - + diff --git a/unit_tests/test_skip_plugin.py b/unit_tests/test_skip_plugin.py index e84bcec..c1dccee 100644 --- a/unit_tests/test_skip_plugin.py +++ b/unit_tests/test_skip_plugin.py @@ -18,7 +18,7 @@ class TestSkipPlugin(unittest.TestCase): sk = Skip() sk.addOptions sk.configure - sk.prepareTestResult + sk.prepareTestResult def test_prepare_patches_result(self): stream = _WritelnDecorator(StringIO()) @@ -60,13 +60,13 @@ class TestSkipPlugin(unittest.TestCase): class NoPatch(unittest.TestResult): def __init__(self): self.errorClasses = {} - + res = NoPatch() sk = Skip() sk.prepareTestResult(res) assert not hasattr(res, '_orig_addError'), \ "Skip patched a result class it didn't need to patch" - + def test_skip_output(self): class TC(unittest.TestCase): @@ -81,10 +81,11 @@ class TestSkipPlugin(unittest.TestCase): test = TC('test') test(res) assert not res.errors, "Skip was not caught: %s" % res.errors - assert res.skipped + assert res.skipped res.printErrors() out = stream.getvalue() + print out assert out assert out.strip() == "S" assert res.wasSuccessful() @@ -94,7 +95,7 @@ class TestSkipPlugin(unittest.TestCase): class TC(unittest.TestCase): def test(self): raise SkipTest('skip me too') - + stream = _WritelnDecorator(StringIO()) res = _TextTestResult(stream, 0, verbosity=2) sk = Skip() @@ -102,7 +103,7 @@ class TestSkipPlugin(unittest.TestCase): test = TC('test') test(res) assert not res.errors, "Skip was not caught: %s" % res.errors - assert res.skipped + assert res.skipped res.printErrors() out = stream.getvalue() |