diff options
-rw-r--r-- | functional_tests/test_loader.py | 2 | ||||
-rw-r--r-- | nose/config.py | 17 | ||||
-rw-r--r-- | nose/core.py | 7 | ||||
-rw-r--r-- | nose/exc.py | 7 | ||||
-rw-r--r-- | nose/plugins/base.py | 28 | ||||
-rw-r--r-- | nose/plugins/builtin.py | 5 | ||||
-rw-r--r-- | nose/plugins/capture.py | 31 | ||||
-rw-r--r-- | nose/plugins/manager.py | 4 | ||||
-rw-r--r-- | nose/proxy.py | 7 | ||||
-rw-r--r-- | unit_tests/test_capture_plugin.py | 44 | ||||
-rw-r--r-- | unit_tests/test_config.py | 2 |
11 files changed, 116 insertions, 38 deletions
diff --git a/functional_tests/test_loader.py b/functional_tests/test_loader.py index 2b84b42..256fac2 100644 --- a/functional_tests/test_loader.py +++ b/functional_tests/test_loader.py @@ -187,6 +187,7 @@ class TestNoseTestLoader(unittest.TestCase): "Expected to run 0 tests but ran %s" % res.testsRun def test_mod_setup_skip_no_tests_run_no_errors(self): + # FIXME need to load a config with builtin plugins ctx = os.path.join(support, 'ctx') l = loader.TestLoader(workingDir=ctx) suite = l.loadTestsFromName('mod_setup_skip.py') @@ -201,6 +202,7 @@ class TestNoseTestLoader(unittest.TestCase): "Expected to run 0 tests but ran %s" % res.testsRun def test_mod_import_skip_one_test_no_errors(self): + # FIXME need to load a config with builtin plugins ctx = os.path.join(support, 'ctx') l = loader.TestLoader(workingDir=ctx) suite = l.loadTestsFromName('mod_import_skip.py') diff --git a/nose/config.py b/nose/config.py index 8b94d94..bcee140 100644 --- a/nose/config.py +++ b/nose/config.py @@ -12,10 +12,7 @@ class Config(object): self.testMatch = re.compile(r'(?:^|[\b_\.%s-])[Tt]est' % os.sep) self.addPaths = True - self.capture = True self.detailedErrors = False - self.debugErrors = False - self.debugFailures = False self.exclude = None self.exit = True self.includeExe = sys.platform=='win32' @@ -39,7 +36,6 @@ class Config(object): self.args = () self.testMatch = re.compile(r'(?:^|[\b_\.%s-])[Tt]est' % os.sep) self.addPaths = not env.get('NOSE_NOPATH', False) - self.capture = not env.get('NOSE_NOCAPTURE', False) self.detailedErrors = env.get('NOSE_DETAILED_ERRORS', False) self.debug = env.get('NOSE_DEBUG') self.debugLog = env.get('NOSE_DEBUG_LOG') @@ -117,10 +113,7 @@ class Config(object): self.configureLogging(options) self.addPaths = options.addPaths - self.capture = options.capture self.detailedErrors = options.detailedErrors - self.debugErrors = options.debugErrors - self.debugFailures = options.debugFailures self.stopOnError = options.stopOnError self.verbosity = options.verbosity self.includeExe = options.includeExe @@ -153,7 +146,7 @@ class Config(object): def configureLogging(self, options): # FIXME - # logging.basicConfig(level=logging.DEBUG) + # xlogging.basicConfig(level=logging.DEBUG) pass def default(self): @@ -188,8 +181,7 @@ class Config(object): help="Log debug messages to this file " "(default: sys.stderr)") parser.add_option( - "-q", "--quiet", action="store_const", - const=0, dest="verbosity") + "-q", "--quiet", action="store_const", const=0, dest="verbosity") parser.add_option( "-w", "--where", action="append", dest="where", help="DEPRECATED Look for tests in this directory. " @@ -205,11 +197,6 @@ class Config(object): help="Also run tests that match regular " "expression [NOSE_INCLUDE]") parser.add_option( - "-s", "--nocapture", action="store_false", - default=self.capture, dest="capture", - help="Don't capture stdout (any stdout output " - "will be printed immediately) [NOSE_NOCAPTURE]") - parser.add_option( "-d", "--detailed-errors", action="store_true", default=self.detailedErrors, dest="detailedErrors", help="Add detail to error" diff --git a/nose/core.py b/nose/core.py index 13ac2e7..3f06827 100644 --- a/nose/core.py +++ b/nose/core.py @@ -14,7 +14,7 @@ from nose.importer import add_path from nose.loader import defaultTestLoader from nose.plugins.manager import DefaultPluginManager from nose.result import TextTestResult -from nose.util import absdir, tolist, start_capture, end_capture +from nose.util import absdir, tolist log = logging.getLogger('nose.core') @@ -230,11 +230,6 @@ class TestProgram(unittest.TestProgram): log.debug("configured %s", self.config) - # call early to ensure we get our hooks into sys.stdout before - # any subject modules are loaded and import it - if self.config.capture: - start_capture() - # instantiate the test loader if self.testLoader is None: self.testLoader = defaultTestLoader(config=self.config) diff --git a/nose/exc.py b/nose/exc.py index b0cd2dd..851c5df 100644 --- a/nose/exc.py +++ b/nose/exc.py @@ -1,11 +1,8 @@ """Exceptions for marking tests as skipped or deprecated. """ +from nose.plugins.skip import SkipTest + class DeprecatedTest(Exception): """Raise this exception to mark a test as deprecated. """ pass - -class SkipTest(Exception): - """Raise this exception to mark a test as skipped. - """ - pass diff --git a/nose/plugins/base.py b/nose/plugins/base.py index 16abe73..a4810ad 100644 --- a/nose/plugins/base.py +++ b/nose/plugins/base.py @@ -268,6 +268,34 @@ class IPluginInterface(object): """ pass + def formatError(self, test, err): + """Called in result.addError, before plugin.addError. If you + want to replace or modify the error tuple, return a new error + tuple. + + Parameters: + * test: + the test case + * err: + the error tuple (class, value, traceback) + """ + pass + formatError._new = True + + def formatFailure(self, test, err): + """Called in result.addFailure, before plugin.addFailure. If you + want to replace or modify the error tuple, return a new error + tuple. + + Parameters: + * test: + the test case + * err: + the error tuple (class, value, traceback) + """ + pass + formatFailure._new = True + def handleError(self, test, err): """Called on addError. To handle the error yourself and prevent normal error processing, return a true value. diff --git a/nose/plugins/builtin.py b/nose/plugins/builtin.py index 55253ad..cb5ee17 100644 --- a/nose/plugins/builtin.py +++ b/nose/plugins/builtin.py @@ -3,9 +3,12 @@ Lists builtin plugins """ from nose.plugins.attrib import AttributeSelector +from nose.plugins.capture import Capture from nose.plugins.cover import Coverage +from nose.plugins.debug import Pdb from nose.plugins.doctests import Doctest ## from nose.plugins.isolation import from nose.plugins.prof import Profile +from nose.plugins.skip import Skip -plugins = [AttributeSelector, Coverage, Doctest, Profile] +plugins = [AttributeSelector, Capture, Coverage, Doctest, Pdb, Profile, Skip] diff --git a/nose/plugins/capture.py b/nose/plugins/capture.py index 350abaa..2819780 100644 --- a/nose/plugins/capture.py +++ b/nose/plugins/capture.py @@ -1,3 +1,4 @@ +import os import sys from nose.plugins.base import Plugin from nose.util import ln @@ -8,14 +9,40 @@ except ImportError: class Capture(Plugin): - + """Output capture plugin. Enabled by default. Disable with -s or + --nocapture. This plugin captures stdout during test execution, + appending any output capture to the error or failure output, + should the test fail or raise an error. + """ + enabled = True + env_opt = 'NOSE_NOCAPTURE' + name = 'capture' + def __init__(self): self.stdout = None self._buf = None + def options(self, parser, env=os.environ): + parser.add_option( + "-s", "--nocapture", action="store_false", + default=not env.get(self.env_opt), dest="capture", + help="Don't capture stdout (any stdout output " + "will be printed immediately) [NOSE_NOCAPTURE]") + + def configure(self, options, conf): + self.conf = conf + if not options.capture: + self.enabled = False + + def afterTest(self, test): + self.end() + def begin(self): self.start() # get an early handle on sys.stdout + def beforeTest(self, test): + self.start() + def formatError(self, test, err): self.end() test.captured_output = output = self.buffer @@ -25,7 +52,7 @@ class Capture(Plugin): return (ec, self.addCaptureToErr(ev, output), tb) def formatFailure(self, test, err): - return self.formatError(self, test, err) + return self.formatError(test, err) def addCaptureToErr(self, ev, output): return '\n'.join([str(ev) , ln('>> begin captured stdout <<'), diff --git a/nose/plugins/manager.py b/nose/plugins/manager.py index 2c67d1d..7b85798 100644 --- a/nose/plugins/manager.py +++ b/nose/plugins/manager.py @@ -44,11 +44,13 @@ class PluginManager(object): and config instance. After configuration, disabled plugins are removed from the plugins list. """ + log.debug("Configuring plugins") self.config = config cfg = PluginProxy('configure', self._plugins) cfg(options, config) enabled = [plug for plug in self._plugins if plug.enabled] - self.plugins = enabled + self.plugins = enabled + log.debug("Plugins enabled: %s", enabled) def loadPlugins(self): pass diff --git a/nose/proxy.py b/nose/proxy.py index b2ff945..a2cdca8 100644 --- a/nose/proxy.py +++ b/nose/proxy.py @@ -83,9 +83,10 @@ class ResultProxy(object): def assertMyTest(self, test): # The test I was called with must be my .test or my # .test's .test. - assert test is getattr(self.test, 'test', self.test), \ - "ResultProxy for %r was called with test %r" \ - % (self.test, test) + + assert test is self.test or test is getattr(self.test, 'test', 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) diff --git a/unit_tests/test_capture_plugin.py b/unit_tests/test_capture_plugin.py index 0504f98..58cc2ec 100644 --- a/unit_tests/test_capture_plugin.py +++ b/unit_tests/test_capture_plugin.py @@ -1,9 +1,46 @@ import sys import unittest +from optparse import OptionParser +from nose.config import Config from nose.plugins.capture import Capture class TestCapturePlugin(unittest.TestCase): + def test_enabled_by_default(self): + c = Capture() + assert c.enabled + + def test_can_be_disabled(self): + c = Capture() + parser = OptionParser() + c.addOptions(parser) + options, args = parser.parse_args(['test_can_be_disabled', + '-s']) + c.configure(options, Config()) + assert not c.enabled + + c = Capture() + options, args = parser.parse_args(['test_can_be_disabled_long', + '--nocapture']) + c.configure(options, Config()) + assert not c.enabled + + env = {'NOSE_NOCAPTURE': 1} + c = Capture() + parser = OptionParser() + c.addOptions(parser, env) + options, args = parser.parse_args(['test_can_be_disabled']) + c.configure(options, Config()) + assert not c.enabled + + c = Capture() + parser = OptionParser() + c.addOptions(parser) + + options, args = parser.parse_args(['test_can_be_disabled']) + c.configure(options, Config()) + assert c.enabled + def test_captures_stdout(self): c = Capture() c.start() @@ -25,11 +62,12 @@ class TestCapturePlugin(unittest.TestCase): formatted = c.formatError(d, err) ec, ev, tb = err fec, fev, ftb = formatted + # print fec, fev, ftb + self.assertEqual(ec, fec) self.assertEqual(tb, ftb) - - assert 'Oh my!' in fev - assert 'Oh my!' in d.captured_output + assert 'Oh my!' in fev, "Output not found in error message" + assert 'Oh my!' in d.captured_output, "Output not attached to test" if __name__ == '__main__': unittest.main() diff --git a/unit_tests/test_config.py b/unit_tests/test_config.py index 660d298..2378bf6 100644 --- a/unit_tests/test_config.py +++ b/unit_tests/test_config.py @@ -7,8 +7,6 @@ class TestNoseConfig(unittest.TestCase): def test_defaults(self): c = nose.config.Config() assert c.addPaths == True - assert c.capture == True - assert c.detailedErrors == False # FIXME etc def test_reset(self): |