Changeset 15
- Timestamp:
- Mon Mar 27 22:59:52 2006
- Files:
-
- trunk/nose/core.py (modified) (diff)
- trunk/nose/suite.py (modified) (diff)
- trunk/nose/plugins/doctests.py (modified) (diff)
- trunk/nose/plugins/base.py (modified) (diff)
- trunk/nose/plugins/attrib.py (modified) (diff)
- trunk/nose/util.py (modified) (diff)
- trunk/nose/result.py (modified) (diff)
- trunk/nose/loader.py (modified) (diff)
- trunk/st/unit_tests/test_plugins.py (modified) (diff)
- trunk/examples/html_plugin (added)
- trunk/examples/html_plugin/htmlplug.py (added)
- trunk/examples/html_plugin/Example_html_output_plugin.egg-info (added)
- trunk/examples/html_plugin/Example_html_output_plugin.egg-info/SOURCES.txt (added)
- trunk/examples/html_plugin/Example_html_output_plugin.egg-info/top_level.txt (added)
- trunk/examples/html_plugin/Example_html_output_plugin.egg-info/PKG-INFO (added)
- trunk/examples/html_plugin/Example_html_output_plugin.egg-info/entry_points.txt (added)
- trunk/examples/html_plugin/setup.py (added)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
trunk/nose/core.py
r14 r15 13 13 TextTestResult 14 14 from nose.config import Config 15 from nose.importer import _import16 15 from nose.loader import defaultTestLoader 17 16 from nose.selector import defaultSelector … … 21 20 log = logging.getLogger('nose.core') 22 21 23 # backwards compatibility24 TestCase = unittest.TestCase25 26 class FunctionTestCase(TestCase):27 """TestCase wrapper for functional tests.28 29 Don't use this class directly; it is used internally in nose to30 create test cases for functional tests.31 32 This class is very similar to unittest.FunctionTestCase, with a few33 extensions:34 * It descends from nose.TestCase, and so provides output35 capture36 * It allows setup and teardown functions to be defined as attributes37 of the test function. A convenient way to set this up is via the38 provided with_setup decorator:39 40 def setup_func():41 # ...42 43 def teardown_func():44 # ...45 46 @with_setup(setup_func, teardown_func)47 def test_something():48 # ...49 50 """51 def __init__(self, testFunc, setUp=None, tearDown=None, description=None,52 fromDirectory=None):53 TestCase.__init__(self)54 self.testFunc = testFunc55 self.setUpFunc = setUp56 self.tearDownFunc = tearDown57 self.description = description58 self.fromDirectory = fromDirectory59 60 def id(self):61 name = self.testFunc.__name__62 if self.fromDirectory is None:63 return name64 else:65 return "%s: %s" % (self.fromDirectory, name)66 67 def runTest(self):68 self.testFunc()69 70 def setUp(self):71 """Run any setup function attached to the test function72 """73 if self.setUpFunc:74 self.setUpFunc()75 else:76 names = ('setup', 'setUp', 'setUpFunc')77 try_run(self.testFunc, names)78 79 def tearDown(self):80 """Run any teardown function attached to the test function81 """82 if self.tearDownFunc:83 self.tearDownFunc()84 else:85 names = ('teardown', 'tearDown', 'tearDownFunc')86 try_run(self.testFunc, names)87 88 def __str__(self):89 if hasattr(self.testFunc, 'compat_func_name'):90 name = self.testFunc.compat_func_name91 else:92 name = self.testFunc.__name__93 name = "%s.%s" % (self.testFunc.__module__, name)94 if self.fromDirectory is None:95 return name96 else:97 return "%s: %s" % (self.fromDirectory, name)98 __repr__ = __str__99 100 def shortDescription(self):101 pass # FIXME102 103 104 105 class MethodTestCase(TestCase):106 """Test case that wraps one method in a test class.107 """108 def __init__(self, cls, method):109 self.cls = cls110 self.method = method111 self.testInstance = self.cls()112 self.testCase = getattr(self.testInstance, self.method)113 super(MethodTestCase, self).__init__()114 115 def __str__(self):116 return self.id()117 118 def id(self):119 return "%s.%s.%s" % (self.cls.__module__,120 self.cls.__name__,121 self.method)122 123 def setUp(self):124 """Run any setup method declared in the test class to which this125 method belongs126 """127 names = ('setup', 'setUp')128 try_run(self.testInstance, names)129 130 def runTest(self):131 self.testCase()132 133 def tearDown(self):134 """Run any teardown method declared in the test class to which135 this method belongs136 """137 if self.testInstance is not None:138 names = ('teardown', 'tearDown')139 try_run(self.testInstance, names)140 141 def shortDescription(self):142 # FIXME ... diff output if is TestCase subclass, for back compat143 if self.testCase.__doc__ is not None:144 return '(%s.%s) "%s"' % (self.cls.__module__,145 self.cls.__name__,146 self.testCase.__doc__)147 return None148 149 22 150 23 class TestCollector(LazySuite): … … 168 41 self.loader = loader 169 42 self.path = conf.where 170 self.plugins = conf.plugins171 43 172 44 def loadtests(self): … … 212 84 if wrapper is not None: 213 85 test = wrapper 214 return super(TextTestRunner, self).run(test) 86 87 # plugins can decorate or capture the output stream 88 wrapped = call_plugins(self.conf.plugins, 'setOutputStream', 89 self.stream) 90 if wrapped is not None: 91 self.stream = wrapped 92 93 result = super(TextTestRunner, self).run(test) 94 call_plugins(self.conf.plugins, 'finalize', result) 95 return result 215 96 216 97 -
trunk/nose/suite.py
r14 r15 1 1 import logging 2 import sys 2 3 import unittest 3 4 from nose.config import Config -
trunk/nose/plugins/doctests.py
r14 r15 68 68 yield test 69 69 70 def loadTestsFrom Name(self, filename, package=None, importPath=None):70 def loadTestsFromPath(self, filename, package=None, importPath=None): 70 70 if self.extension and anyp(filename.endswith, self.extension): 71 71 try: -
trunk/nose/plugins/base.py
r14 r15 37 37 """ 38 38 env_opt = 'NOSE_WITH_%s' % self.name.upper() 39 env_opt.replace('-', '_') 39 40 parser.add_option("--with-%s" % self.name, 40 41 action="store_true", … … 96 97 When plugins are called, the first plugin that implements a method 97 98 and returns a non-None value wins, and plugin processing ends. 99 100 In general, plugin methods correspond directly to the methods of 101 nose.selector.Selector, nose.loader.TestLoader and 102 nose.result.TextTestResult where they are called. In some cases, the 103 plugin hook doesn't neatly match the method in which it is called; 104 for those, the documentation for the hook will tell you where in the 105 test process it is available. 106 107 Plugin hooks fall into two broad categories: selecting and loading 108 tests, and watching and reporting on test results. 109 110 Selecting and loading tests 111 =========================== 112 113 To alter test selection behavior, implement any necessary want* 114 methods as outlined below. Keep in mind, though, that when your 115 plugin returns True from a want* method, you will send the requested 116 object through the normal test collection process. If the object 117 represents something from which normal tests can't be collected, you 118 must also implement a loader method to load the tests. 119 120 Examples: 121 * The builtin doctests plugin, for python 2.4 only, implements 122 `wantFile` to enable loading of doctests from files that are not 123 python modules. It also implements `loadTestsFromModule` to load 124 doctests from python modules, and `loadTestsFromPath` to load tests 125 from the non-module files selected by `wantFile`. 126 * The builtin attrib plugin implements `wantFunction` and 127 `wantMethod` so that it can reject tests that don't match the 128 specified attributes. 129 130 Watching or reporting on tests 131 ============================== 132 133 To record information about tests or other modules imported during 134 the testing process, output additional reports, or entirely change 135 test report output, implement any of the methods outlined below that 136 correspond to TextTestResult methods. 137 138 Examples: 139 * The builtin cover plugin implements `begin` and `report` to 140 capture and report code coverage metrics for all or selected modules 141 loaded during testing. 142 * The builtin profile plugin implements `begin`, `prepareTest` and 143 `report` to record and output profiling information. In this 144 case, the plugin's `prepareTest` method constructs a function that 145 runs the test through the hotshot profiler's runcall() method. 98 146 """ 99 147 def __new__(cls, *arg, **kw): … … 124 172 pass 125 173 126 def addFailure(self, test, err): 127 """Called when a test fails. DO NOT return a value unless you want to 128 stop other plugins from seeing that the test has failed. 174 def addFailure(self, test, err, tb_info): 175 """Called when a test fails. DO NOT return a value unless you 176 want to stop other plugins from seeing that the test has failed. 129 177 130 178 Parameters: … … 133 181 * err: 134 182 sys.exc_info() tuple 183 * tb_info: 184 Introspected traceback info, if any 135 185 """ 136 186 pass 137 187 138 188 def addSkip(self, test): 139 """Called when a test is skipped. DO NOT return a value unless you want 140 to stop other plugins from seeing the skipped test. 189 """Called when a test is skipped. DO NOT return a value unless 190 you want to stop other plugins from seeing the skipped test. 141 191 142 192 Parameters: … … 147 197 148 198 def addSuccess(self, test): 149 """Called when a test passes. DO NOT return a value unless you want to 150 stop other plugins from seeing the passing test. 199 """Called when a test passes. DO NOT return a value unless you 200 want to stop other plugins from seeing the passing test. 151 201 152 202 Parameters: … … 162 212 pass 163 213 214 def finalize(self, result): 215 """Called after all report output, including output from all plugins, 216 has been sent to the stream. Use this to print final test 217 results. Return None to allow other plugins to continue 218 printing, any other value to stop them. 219 """ 220 pass 221 164 222 def loadTestsFromModule(self, module): 165 """Return iterable of tests in a module. May be a generator. Each item 166 returned must be a runnable unittest.TestCase subclass. Return 167 None if your plugin cannot collect any tests from module. 223 """Return iterable of tests in a module. May be a 224 generator. Each item returned must be a runnable 225 unittest.TestCase subclass. Return None if your plugin cannot 226 collect any tests from module. 168 227 169 228 Parameters: … … 205 264 206 265 def loadTestsFromTestCase(self, cls): 207 """Return tests in this test case class. Return None if you are not able 208 to load any tests, or an iterable if you are. May be a 266 """Return tests in this test case class. Return None if you are 267 not able to load any tests, or an iterable if you are. May be a 209 268 generator. 210 269 … … 235 294 """ 236 295 pass 296 297 def setOutputStream(self, stream): 298 """Called before test output begins. To direct test output to a 299 new stream, return a stream object, which must implement a 300 write(msg) method. If you only want to note the stream, not 301 capture or redirect it, then return None. 302 303 Parameters: 304 * stream: 305 the original output stream 306 """ 237 307 238 308 def startTest(self, test): … … 269 339 directory, false if you do not, and None if you don't care. 270 340 341 FIXME: WARNING: Hook not implemented! 342 271 343 Parameters: 272 344 * dirname: … … 310 382 """Return true if you want to collection to descend into this 311 383 module, false to prevent the collector from descending into the 312 module, and None if you rdon't care.384 module, and None if you don't care. 312 384 313 385 Parameters: -
trunk/nose/plugins/attrib.py
r14 r15 68 68 attr and eval_attr may each be lists. 69 69 """ 70 70 self.attribs = [] 71 71 72 # handle python eval-expression parameter 72 73 if options.eval_attr: … … 101 102 # -> 'bool(obj.name)' must be True 102 103 value = True 103 self.attribs.append((key, value)) 104 self.attribs.append((key, value)) 103 104 if self.attribs: 104 105 self.enabled = True -
trunk/nose/util.py
r14 r15 2 2 """ 3 3 import inspect 4 import logging 4 5 import os 5 6 import sys 6 7 import types 7 8 9 log = logging.getLogger('nose') 10 8 11 try: 9 12 from cStringIO import StringIO -
trunk/nose/result.py
r14 r15 17 17 _TextTestResult = unittest._TextTestResult 18 18 19 # FIXME addSuccess needed here20 19 21 20 class TextTestResult(_TextTestResult): … … 40 39 whole to be considered non-successful. 41 40 """ 42 def __init__(self, stream, descriptions, verbosity, conf): 43 _TextTestResult.__init__(self, stream, descriptions, verbosity) 41 separator3 = '.' * 70 42 43 def __init__(self, stream, descriptions, verbosity, conf): 44 44 self.deprecated = [] 45 45 self.skip = [] 46 46 self.conf = conf 47 47 self.capture = conf.capture 48 49 def startTest(self, test): 50 if self.capture: 51 self.resetBuffer() 52 super(TextTestResult, self).startTest(test) 53 call_plugins(self.conf.plugins, 'startTest', test) 54 55 def stopTest(self, test): 56 if self.capture: 57 self.resetBuffer() 58 super(TextTestResult, self).stopTest(test) 59 call_plugins(self.conf.plugins, 'stopTest', test) 60 48 _TextTestResult.__init__(self, stream, descriptions, verbosity) 49 61 50 def addDeprecated(self, test): 62 51 self.deprecated.append((test, '', '')) … … 82 71 self.resetBuffer() 83 72 self.writeRes('ERROR','E') 84 call_plugins(self.conf.plugins, 'addError', test) 73 call_plugins(self.conf.plugins, 'addError', test, err) 84 73 85 74 def addFailure(self, test, err): … … 103 92 self.resetBuffer() 104 93 self.writeRes('FAIL','F') 105 call_plugins(self.conf.plugins, 'addFailure', test) 94 call_plugins(self.conf.plugins, 'addFailure', test, err, tb) 105 94 106 95 def addSkip(self, test): … … 110 99 self.writeRes('SKIP','S') 111 100 call_plugins(self.conf.plugins, 'addSkip', test) 112 113 def resetBuffer(self):114 buffer.truncate(0)115 101 102 def addSuccess(self, test): 103 self.resetBuffer() 104 self.writeRes('ok', '.') 105 call_plugins(self.conf.plugins, 'addSuccess', test) 106 116 107 def printErrors(self): 117 108 super(TextTestResult, self).printErrors() … … 131 122 self.stream.writeln(ln('>> end captured stdout <<')) 132 123 124 def resetBuffer(self): 125 buffer.truncate(0) 126 127 def startTest(self, test): 128 if self.capture: 129 self.resetBuffer() 130 super(TextTestResult, self).startTest(test) 131 call_plugins(self.conf.plugins, 'startTest', test) 132 133 def stopTest(self, test): 134 if self.capture: 135 self.resetBuffer() 136 super(TextTestResult, self).stopTest(test) 137 call_plugins(self.conf.plugins, 'stopTest', test) 138 133 139 def writeRes(self, long, short): 134 140 if self.showAll: -
trunk/nose/loader.py
r14 r15 8 8 from nose.plugins import call_plugins 9 9 from nose.suite import LazySuite, TestClass, TestModule 10 from nose.util import split_test_name 10 from nose.util import split_test_name, try_run 10 10 11 11 log = logging.getLogger(__name__) … … 39 39 def __init__(self, testFunc, setUp=None, tearDown=None, description=None, 40 40 fromDirectory=None): 41 TestCase.__init__(self)41 super(FunctionTestCase, self).__init__() 41 41 self.testFunc = testFunc 42 42 self.setUpFunc = setUp … … 186 186 if self.selector.wantModuleTests(module): 187 187 log.debug("load tests from %s", module.__name__) 188 for test in self.testsInModule(module): 188 for test in self.testsInModule(module, importPath): 188 188 tests.append(test) 189 189 … … 352 352 yield MethodTestCase(cls, item) 353 353 354 def testsInModule(self, module): 354 def testsInModule(self, module, importPath=None): 354 354 """Find functions and classes matching testMatch, as well as 355 355 classes that descend from unittest.TestCase, return all found … … 389 389 # run functional tests in the order in which they are defined 390 390 func_tests.sort(cmp_line) 391 tests.extend([ FunctionTestCase(test, 392 fromDirectory=self.fromDirectory) 393 for test in func_tests ]) 391 tests.extend([ FunctionTestCase(test, fromDirectory=importPath) 392 for test in func_tests ]) 394 393 return tests 395 394 -
trunk/st/unit_tests/test_plugins.py
r14 r15 157 157 plug.configure(opt, conf) 158 158 plug.extension = ['.txt'] 159 suite = plug.loadTestsFrom Name(fn)159 suite = plug.loadTestsFromPath(fn) 159 159 for test in suite: 160 160 assert str(test).endswith('doctests.txt') … … 163 163 class TestAttribPlugin(unittest.TestCase): 164 164 165 def test_add_options(self): 166 plug = AttributeSelector() 167 parser = MockOptParser() 168 plug.add_options(parser) 169 170 expect = [(('-a', '--attr'), 171 {'dest': 'attr', 'action': 'append', 'default': None, 172 'help': 'Run only tests that have attributes ' 173 'specified by ATTR [NOSE_ATTR]'}), 174 (('-A', '--eval-attr'), 175 {'dest': 'eval_attr', 'action': 'append', 176 'default': None, 'metavar': 'EXPR', 177 'help': 'Run only tests for whose attributes the ' 178 'Python expression EXPR evaluates to True ' 179 '[NOSE_EVAL_ATTR]'})] 180 self.assertEqual(parser.opts, expect) 181 182 opt = Bucket() 183 opt.attr = ['!slow'] 184 plug.configure(opt, Config()) 185 assert plug.enabled 186 self.assertEqual(plug.attribs, [('slow',False)]) 187 188 opt.attr = ['fast,quick', 'weird=66'] 189 plug.configure(opt, Config()) 190 self.assertEqual(plug.attribs, [('fast', True), 191 ('quick', True), 192 ('weird', '66')]) 193 194 opt.attr = None 195 opt.eval_attr = [ 'weird >= 66' ] 196 plug.configure(opt, Config()) 197 self.assertEqual(plug.attribs[0][0], 'weird >= 66') 198 self.assertTrue(callable(plug.attribs[0][1])) 199 165 200 def test_basic_attr(self): 166 201 def f():
