Changeset 24
- Timestamp:
- Wed Apr 19 20:34:11 2006
- Files:
-
- trunk/AUTHORS (modified) (diff)
- trunk/CHANGELOG (modified) (diff)
- trunk/unit_tests/test_loader.py (modified) (diff)
- trunk/unit_tests/test_inspector.py (modified) (diff)
- trunk/unit_tests/test_result.py (modified) (diff)
- trunk/unit_tests/test_plugins.py (modified) (diff)
- trunk/unit_tests/test_collector.py (modified) (diff)
- trunk/unit_tests/test_lazy_suite.py (modified) (diff)
- trunk/nose/suite.py (modified) (diff)
- trunk/nose/plugins/attrib.py (modified) (diff)
- trunk/nose/result.py (modified) (diff)
- trunk/nose/config.py (modified) (diff)
- trunk/nose/inspector.py (modified) (diff)
- trunk/NEWS (modified) (diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
trunk/AUTHORS
r6 r24 1 1 Jason Pellerin 2 2 Kumar McMillan 3 Mika Eloranta3 Mika Eloranta 4 Jay Parlar 5 Scot Doyle -
trunk/CHANGELOG
r4 r24 1 0.9.0a1 2 3 - Add support for plugins, with hooks for selecting, loading and reporting on 4 tests. Doctest and coverage are now plugins. 5 - Add builtin plugins for profiling with hotshot, selecting tests by 6 attribute (contributed by Mika Eloranta), and warning of missed tests 7 specified on command line. 8 - Change command line test selection syntax to match unittest. Thanks to Titus 9 Brown for the suggestion. 10 - Option to drop into pdb on error or failure. 11 - Option to stop running on first error or failure. Thanks to Kevin Dangoor 12 for the suggestion. 13 - Support for doctests in files other than python modules (python 2.4 only) 14 - Reimplement base test selection as single self-contained class. 15 - Reimplement test loading as unittest-compatible TestLoader class. 16 - Remove all monkeypatching. 17 - Reimplement output capture and assert introspection support in 18 unittest-compatible Result class. 19 - Better support for multiline constructions in assert introspection. 20 - More context output with assert introspections. 21 - Refactor setuptools test command support to use proxied result, which 22 enables output capture and assert introspection support without 23 monkeypatching. 24 - Add support for generators in test classes. Thanks to Jay Parlar for the 25 suggestion and patch. 26 - Add nose.tools package with some helpful test-composition functions and 27 decorators, including @raises, contributed by Scot Doyle. 28 - Reimplement nose.main (TestProgram) to have unittest-compatible signature. 29 - All-new import path handling. You can even turn it off! (If you don't, 30 nose will ensure that all directories from which it imports anything are on 31 sys.path before the import.) 32 - Logging package used for verbose logging. 33 - Support for skipped and deprecated tests. 34 - Configuration is no longer global. 35 1 36 0.8.7 2 37 -
trunk/unit_tests/test_loader.py
r23 r24 6 6 from nose.importer import _import 7 7 8 from helpers import iter_compat 8 9 from mock import * 9 10 … … 62 63 found = [] 63 64 tests = l.loadTestsFromNames(['test', 'foo.test_foo']) 64 for t in tests:65 for t in iter_compat(tests): 64 65 found.append(str(t)) 65 66 self.assertEqual(found, expect) 66 67 67 68 expect = [ 'test module foo in %s' % self.support ] 68 # found = []69 69 tests = l.loadTestsFromNames(None, module=foo) 70 70 found = [ str(tests) ] 71 #for t in tests:72 # found.append(str(t))73 71 self.assertEqual(found, expect) 74 72 75 #found = []76 #Etests = l.loadTestsFromNames(None, module=foo)77 #Efor t in tests:78 # found.append(str(t))79 #self.assertEqual(found, expect)80 73 81 74 def test_load_from_names_compat(self): … … 93 86 found = [] 94 87 # print tests 95 for test in tests:88 for test in iter_compat(tests): 95 88 # print test 96 for t in test:89 for t in iter_compat(test): 96 89 # print t 97 90 found.append(str(t)) … … 105 98 names[0] = ':' + names[0] 106 99 tests = l.loadTestsFromNames(names, sys.modules[__name__]) 107 for test in tests: 108 for t in test: 100 for test in iter_compat(tests): 101 for t in iter_compat(test): 109 102 found.append(str(t)) 110 103 self.assertEqual(found, expect) … … 168 161 cases = l.loadTestsFromTestCase(TC) 169 162 count = 0 170 for suite in cases: 171 for case in suite: 163 for suite in iter_compat(cases): 164 for case in iter_compat(suite): 172 165 assert str(case) == '%s.TC.test_generator:(%d,)' % \ 173 166 (__name__, count) … … 191 184 print cases 192 185 count = 0 193 for case in cases:186 for case in iter_compat(cases): 193 186 # print case 194 187 self.assertEqual(str(case), … … 199 192 200 193 if __name__ == '__main__': 201 import logging 202 logging.basicConfig() 203 logging.getLogger('').setLevel(0) 204 unittest.main() # testLoader=loader.TestLoader()) 194 #import logging 195 #logging.basicConfig() 196 #logging.getLogger('').setLevel(0) 197 unittest.main(testLoader=loader.TestLoader()) -
trunk/unit_tests/test_inspector.py
r21 r24 11 11 from StringIO import StringIO 12 12 13 from nose.inspector import inspect_traceback, Expander 13 from nose.inspector import inspect_traceback, Expander, tbsource 13 13 14 14 class TestExpander(unittest.TestCase): … … 23 23 tokenize.tokenize(src.readline, exp) 24 24 # print "'%s'" % exp.expanded_source 25 assert exp.expanded_source.strip() == '2 > 2'25 self.assertEqual(exp.expanded_source.strip(), '2 > 2') 25 25 26 26 def test_inspect_traceback_continued(self): … … 35 35 out = inspect_traceback(tb) 36 36 # print "'%s'" % out.strip() 37 assert out.strip() == '>> assert 6 < 1, \\\n "This is a multline expression"' 37 self.assertEqual(out.strip(), 38 '>> assert 6 < 1, \\\n ' 39 '"This is a multline expression"') 38 40 41 def test_get_tb_source_simple(self): 42 # no func frame 43 try: 44 assert False 45 except AssertionError: 46 et, ev, tb = sys.exc_info() 47 lines, lineno = tbsource(tb, 1) 48 self.assertEqual(''.join(lines).strip(), 'assert False') 49 self.assertEqual(lineno, 0) 50 51 def test_get_tb_source_func(self): 52 # func frame 53 def check_even(n): 54 print n 55 assert n % 2 == 0 56 try: 57 check_even(1) 58 except AssertionError: 59 et, ev, tb = sys.exc_info() 60 lines, lineno = tbsource(tb) 61 out = textwrap.dedent(''.join(lines)) 62 self.assertEqual(out, 63 ' print n\n' 64 ' assert n % 2 == 0\n' 65 'try:\n' 66 ' check_even(1)\n' 67 'except AssertionError:\n' 68 ' et, ev, tb = sys.exc_info()\n' 69 ) 70 self.assertEqual(lineno, 3) 71 72 # FIXME 2 func frames 73 39 74 def test_pick_tb_lines(self): 40 75 try: … … 47 82 out = inspect_traceback(tb) 48 83 # print "'%s'" % out.strip() 49 assert out.strip() == ">> assert defred('fred') == 'barney', " \ 50 '"Fred - fred != barney?"' 84 self.assertEqual(out.strip(), 85 ">> assert defred('fred') == 'barney', " 86 '"Fred - fred != barney?"') 51 87 try: 52 88 val = "fred" … … 60 96 et, ev, tb = sys.exc_info() 61 97 out = inspect_traceback(tb) 62 # print out 63 assert out.strip() == ">> assert defred('fred') == 'barney', " \ 64 '\\\n "Fred - fred != barney?"' 98 #print "'%s'" % out.strip() 99 self.assertEqual(out.strip(), 100 ">> assert defred('fred') == 'barney', " 101 '\\\n "Fred - fred != barney?"') 65 102 66 103 S = {'setup':1} … … 74 111 et, ev, tb = sys.exc_info() 75 112 out = inspect_traceback(tb) 76 # print "'%s'" % out.strip() 77 assert out.strip() == "assert {'setup': 1}['setup']\n" \ 78 " print 1, 3\n"\ 79 ">> assert 1 % 2 == 0 or 3 % 2 == 0" 113 print "'%s'" % out.strip() 114 self.assertEqual(out.strip(), 115 "assert {'setup': 1}['setup']\n" 116 " print 1, 3\n" 117 ">> assert 1 % 2 == 0 or 3 % 2 == 0") 80 118 81 119 82 120 if __name__ == '__main__': 83 import logging 84 logging.basicConfig() 85 logging.getLogger('').setLevel(0) 121 #import logging 122 #logging.basicConfig() 123 #logging.getLogger('').setLevel(0) 86 124 unittest.main() 87 125 -
trunk/unit_tests/test_result.py
r23 r24 115 115 test = self.T() 116 116 tr.addError(test, err) 117 self.assertTrue(tr.shouldStop)117 assert tr.shouldStop 117 117 118 118 def test_stop_on_error_fail(self): … … 126 126 test = self.T() 127 127 tr.addFailure(test, err) 128 self.assertTrue(tr.shouldStop)128 assert tr.shouldStop 128 128 129 129 if __name__ == '__main__': -
trunk/unit_tests/test_plugins.py
r23 r24 3 3 import unittest 4 4 import nose.plugins 5 from warnings import warn 6 5 7 from nose.config import Config 6 8 from nose.plugins.attrib import AttributeSelector … … 17 19 pass 18 20 21 # some plugins have 2.4-only features 22 pyvers = sys.version_info 23 compat_24 = pyvers[0] >= 2 and pyvers[1] >= 4 24 19 25 class TestBuiltinPlugins(unittest.TestCase): 20 26 … … 86 92 o2, d2 = parser.opts[1] 87 93 assert o2[0] == '--doctest-tests' 88 89 o3, d3 = parser.opts[2] 90 assert o3[0] == '--doctest-extension' 94 95 if compat_24: 96 o3, d3 = parser.opts[2] 97 assert o3[0] == '--doctest-extension' 98 else: 99 assert len(parser.opts) == 2 91 100 92 101 def test_want_file(self): … … 129 138 plug.configure(opt, conf) 130 139 suite = plug.loadTestsFromModule(foo.bar.buz) 131 expect = ['afunc (foo.bar.buz)'] 140 if compat_24: 141 expect = ['afunc (foo.bar.buz)'] 142 else: 143 expect = ['unittest.FunctionTestCase (runit)'] 132 144 for test in suite: 133 assert str(test) == expect.pop(0)145 self.assertEqual(str(test), expect.pop(0)) 133 145 134 146 def test_collect_txtfile(self): 147 if not compat_24: 148 warn("No support for doctests in files other than python modules" 149 " in python versions older than 2.4") 150 return 135 151 here = os.path.abspath(os.path.dirname(__file__)) 136 152 support = os.path.join(here, 'support') … … 182 198 plug.configure(opt, Config()) 183 199 self.assertEqual(plug.attribs[0][0], 'weird >= 66') 184 self.assertTrue(callable(plug.attribs[0][1]))200 assert callable(plug.attribs[0][1]) 184 200 185 201 def test_basic_attr(self): … … 197 213 198 214 def test_eval_attr(self): 215 if not compat_24: 216 warn("No support for eval attributes in python versions older" 217 " than 2.4") 218 return 199 219 def f(): 200 220 pass -
trunk/unit_tests/test_collector.py
r23 r24 6 6 from nose.config import Config 7 7 from nose.result import TextTestResult 8 from helpers import iter_compat 8 9 9 10 class TestNoseCollector(unittest.TestCase): … … 26 27 found = [] 27 28 28 for test in tc:29 for test in iter_compat(tc): 28 29 found.append(str(test)) 29 30 self.assertEqual(found, expect) … … 57 58 found = [] 58 59 59 for test in tc:60 for test in iter_compat(tc): 59 60 print test 60 61 found.append(str(test)) 61 62 test.setUp() 62 for t in test:63 for t in iter_compat(test): 62 63 print ' ', t 63 64 #test(rr) 64 65 found.append(str(t)) 65 66 try: 66 for tt in t:67 for tt in iter_compat(t): 66 67 print ' ', tt 67 68 found.append(str(tt)) -
trunk/unit_tests/test_lazy_suite.py
r21 r24 1 1 import unittest 2 2 from nose import LazySuite 3 from helpers import iter_compat 3 4 4 5 def gen(): … … 14 15 def test_basic_iteration(self): 15 16 ls = LazySuite(gen) 16 for t in ls:17 for t in iter_compat(ls): 16 17 assert isinstance(t, unittest.TestCase) 17 18 -
trunk/nose/suite.py
r23 r24 158 158 159 159 def loadtests(self): 160 for test in self._loadtests(self.module, self.path): 161 yield test 160 tests = self._loadtests(self.module, self.path) 161 try: 162 for test in tests: 163 yield test 164 except TypeError: 165 # python 2.3: TestSuite not iterable 166 for test in tests._tests: 167 yield test 162 168 163 169 def id(self): -
trunk/nose/plugins/attrib.py
r15 r24 51 51 def add_options(self, parser, env=os.environ): 52 52 """Add command-line options for this plugin.""" 53 54 # FIXME disable in < 2.4 53 55 parser.add_option("-a", "--attr", 54 56 dest="attr", action="append", -
trunk/nose/result.py
r23 r24 93 93 94 94 def resetBuffer(self): 95 buffer.truncate(0) 95 buffer.truncate(0) 96 buffer.seek(0) 96 97 97 98 def startTest(self, test): … … 201 202 self.stream.write(short) 202 203 204 def _exc_info_to_string(self, err, test): 205 try: 206 return _TextTestResult._exc_info_to_string(self, err, test) 207 except TypeError: 208 # 2.3: does not take test arg 209 return _TextTestResult._exc_info_to_string(self, err) 203 210 211 204 212 def start_capture(): 205 213 """Start capturing output to stdout. DOES NOT reset the buffer. -
trunk/nose/config.py
r23 r24 1 import copy2 1 import os 3 2 import re … … 24 23 self.where=None 25 24 self.update(kw) 26 self._orig = copy.deepcopy(self.__dict__)25 self._orig = self.__dict__.copy() 26 25 27 26 def __str__(self): -
trunk/nose/inspector.py
r21 r24 6 6 import textwrap 7 7 import tokenize 8 import traceback 8 9 9 10 try: … … 13 14 14 15 log = logging.getLogger(__name__) 15 16 15 16 def inspect_traceback(tb): 16 # FIXME 10 lines might be enough, or... ? 17 """Inspect a traceback and its frame, returning source for the expression 18 where the exception was raised, with simple variable replacement performed 19 and the line on which the exception was raised marked with '>>' 20 """ 17 21 log.debug('inspect traceback %s', tb) 18 19 frames = inspect.getinnerframes(tb, 10)20 21 # FIXME when running under nosetests, context lines have gone missing22 for frame in frames:23 # print frame24 # print inspect.getframeinfo(frame[0])25 26 log.debug('inspect frame %s', frame)27 28 exp = Expander(frame[0].f_locals, frame[0].f_globals)29 22 30 # figure out the set of lines to grab. 31 inspect_lines, mark_line = find_inspectable_lines(frame[4], frame[5]) 32 src = StringIO(textwrap.dedent(''.join(inspect_lines))) 33 34 # if a token error results, try just doing the one line, 35 # stripped of any \ it might have 36 try: 37 tokenize.tokenize(src.readline, exp) 38 except tokenize.TokenError: 39 pass 23 # we only want the innermost frame, where the exception was raised 24 while tb.tb_next: 25 tb = tb.tb_next 26 27 frame = tb.tb_frame 28 lines, exc_line = tbsource(tb) 29 30 # figure out the set of lines to grab. 31 inspect_lines, mark_line = find_inspectable_lines(lines, exc_line) 32 src = StringIO(textwrap.dedent(''.join(inspect_lines))) 33 34 # FIXME 35 # if a token error results, try just doing the one line, 36 # stripped of any \ it might have 37 exp = Expander(frame.f_locals, frame.f_globals) 38 try: 39 tokenize.tokenize(src.readline, exp) 40 except tokenize.TokenError: 41 pass 40 42 43 padded = [] 41 44 if exp.expanded_source: 42 45 exp_lines = exp.expanded_source.split('\n') 43 padded = []44 46 ep = 0 45 47 for line in exp_lines: … … 50 52 padded.append(' ' + line) 51 53 ep += 1 52 return '\n'.join(padded) 54 return '\n'.join(padded) 55 56 57 def tbsource(tb, context=6): 58 """Get source from a traceback object. 53 59 60 A tuple of two things is returned: a list of lines of context from 61 the source code, and the index of the current line within that list. 62 The optional second argument specifies the number of lines of context 63 to return, which are centered around the current line. 54 64 65 NOTE: 66 67 This is taken from the python 2.4 standard library, since a bug in the 2.3 68 version of inspect prevents it from correctly locating source lines in a 69 traceback frame. 70 """ 71 lineno = tb.tb_lineno 72 frame = tb.tb_frame 73 74 if context > 0: 75 start = lineno - 1 - context//2 76 try: 77 lines, lnum = inspect.findsource(frame) 78 except IOError: 79 lines = index = None 80 else: 81 start = max(start, 1) 82 start = max(0, min(start, len(lines) - context)) 83 lines = lines[start:start+context] 84 index = lineno - 1 - start 85 else: 86 lines = index = None 87 88 return (lines, index) 89 90 55 91 def find_inspectable_lines(lines, pos): 56 92 """Find lines in home that are inspectable. … … 59 95 changes in indent level. 60 96 61 Walk forward up to 3 lines, counting \ separated lines as 1 don't walk97 Walk forward up to 3 lines, counting \ separated lines as 1. Don't walk 61 97 over changes in indent level (unless part of an extended line) 62 98 """ … … 65 101 df = re.compile(r':[\s\n]*$') 66 102 ind = re.compile(r'^(\s*)') 67 inspect = [] 103 toinspect = [] 67 103 home = lines[pos] 68 104 home_indent = ind.match(home).groups()[0] … … 73 109 after = lines[pos+1:min(pos+4, len(lines))] 74 110 75 # print "before", before76 # print "after", after77 111 for line in before: 78 112 if ind.match(line).groups()[0] == home_indent: 79 inspect.append(line) 113 toinspect.append(line) 79 113 else: 80 114 break 81 inspect.reverse() 82 # print "before to inspect", inspect 83 84 inspect.append(home) 85 home_pos = len(inspect)-1 115 toinspect.reverse() 116 toinspect.append(home) 117 home_pos = len(toinspect)-1 86 118 continued = cnt.search(home) 87 119 for line in after: 88 120 if ((continued or ind.match(line).groups()[0] == home_indent) 89 121 and not df.search(line)): 90 inspect.append(line) 122 toinspect.append(line) 90 122 continued = cnt.search(line) 91 123 else: 92 124 break 93 # print inspect 94 return inspect, home_pos 95 125 return toinspect, home_pos 126 127 96 128 class Expander: 97 129 """Simple expression expander. Uses tokenize to find the names and … … 155 187 else: 156 188 self.expanded_source += tok 157 # FIXMEif this is the end of the line and the line ends with189 # if this is the end of the line and the line ends with 157 189 # \, then tack a \ and newline onto the output 158 190 # print line[end[1]:] -
trunk/NEWS
r4 r24 1 New in version 0.8 2 3 As of version 0.8, nose will try to discover and run doctest tests in 4 *non-test* packages that it loads. Doctests are run like any other 5 tests, except that doctest captures stdout itself, so nose is not able 6 to print captured output with failed doctests. 7 1 New in version 0.9.0a1 2 ---------------------- 8 3 9 If you have Ned Batchelder's coverage_ module installed, you may print a 10 coverage report (after the test result output) with the -l or --coverage 11 switch or by setting the NOSE_COVERAGE environment variable. The 12 coverage report will cover any module that nose has imported, except 13 modules matching testMatch. 14 15 16 To aid in integrating nose into other scripts or modules, nose.main() 17 now returns the success or failure status of the test run, and does not 18 exit. This means that nose.main() behaves differently from 19 unittest.main(), so consider this change experimental. nose.run_exit() 20 has been added to support the old behavior, and the nosetests script 21 calls that function. 22 4 nose 0.9 is a major new release of nose. For this reason, I'm releasing it 5 first as an alpha version, 0.9.0a1. Between now and 0.9 final, only bugfixes 6 and documentation improvements will be applied to the stable tree. 23 7 24 Prior to this version, modules that defined a setup method called 25 'setUp' or 'setup' would see their setup method run twice, and teardown 26 method not run at all. 8 Here's a quick rundown on what's new in 0.9. 27 9 28 .. _doctest: http://docs.python.org/lib/module-doctest.html 29 .. _coverage: http://www.nedbatchelder.com/code/modules/coverage.html 10 - Plugins! 11 12 The biggest change is support for plugins using setuptools entrypoints. nose 13 plugins can select and load tests (like the builtin doctest plugin), reject 14
