4 # Copyright (C)2007-2012 Versabanq Innovations Inc. and contributors.
5 # Licensed under the GNU Library General Public License, version 2.
6 # See the included file named LICENSE for license information.
7 # You can get wvtest from: http://github.com/apenwarr/wvtest
17 # Why do we do we need the "!= main" check? Because if you run
18 # wvtest.py as a main program and it imports your test files, then
19 # those test files will try to import the wvtest module recursively.
20 # That actually *works* fine, because we don't run this main program
21 # when we're imported as a module. But you end up with two separate
22 # wvtest modules, the one that gets imported, and the one that's the
23 # main program. Each of them would have duplicated global variables
24 # (most importantly, wvtest._registered), and so screwy things could
25 # happen. Thus, we make the main program module *totally* different
26 # from the imported module. Then we import wvtest (the module) into
27 # wvtest (the main program) here and make sure to refer to the right
28 # versions of global variables.
30 # All this is done just so that wvtest.py can be a single file that's
31 # easy to import into your own applications.
32 if __name__ != '__main__': # we're imported as a module
38 """ Use this decorator (@wvtest) in front of any function you want to
39 run as part of the unit test suite. Then run:
40 python wvtest.py path/to/yourtest.py [other test.py files...]
41 to run all the @wvtest functions in the given file(s).
43 _registered.append(func)
47 def _result(msg, tb, code):
52 (filename, line, func, text) = tb
53 filename = os.path.basename(filename)
54 msg = re.sub(r'\s+', ' ', str(msg))
56 print '! %-70s %s' % ('%s:%-4d %s' % (filename, line, msg),
61 def _check(cond, msg = 'unknown', tb = None):
62 if tb == None: tb = traceback.extract_stack()[-3]
64 _result(msg, tb, 'ok')
66 _result(msg, tb, 'FAILED')
71 (filename, line, func, text) = traceback.extract_stack()[-3]
72 text = re.sub(r'^\w+\((.*)\)(\s*#.*)?$', r'\1', text);
77 ''' Issues a notification. '''
78 return _result(message, traceback.extract_stack()[-3], 'ok')
80 def WVPASS(cond = True):
81 ''' Counts a test failure unless cond is true. '''
82 return _check(cond, _code())
84 def WVFAIL(cond = True):
85 ''' Counts a test failure unless cond is false. '''
86 return _check(not cond, 'NOT(%s)' % _code())
89 ''' Counts a test failure unless a == b. '''
90 return _check(a == b, '%s == %s' % (repr(a), repr(b)))
93 ''' Counts a test failure unless a != b. '''
94 return _check(a != b, '%s != %s' % (repr(a), repr(b)))
97 ''' Counts a test failure unless a < b. '''
98 return _check(a < b, '%s < %s' % (repr(a), repr(b)))
101 ''' Counts a test failure unless a <= b. '''
102 return _check(a <= b, '%s <= %s' % (repr(a), repr(b)))
105 ''' Counts a test failure unless a > b. '''
106 return _check(a > b, '%s > %s' % (repr(a), repr(b)))
109 ''' Counts a test failure unless a >= b. '''
110 return _check(a >= b, '%s >= %s' % (repr(a), repr(b)))
112 def WVEXCEPT(etype, func, *args, **kwargs):
113 ''' Counts a test failure unless func throws an 'etype' exception.
114 You have to spell out the function name and arguments, rather than
115 calling the function yourself, so that WVEXCEPT can run before
116 your test code throws an exception.
119 func(*args, **kwargs)
121 return _check(True, 'EXCEPT(%s)' % _code())
123 _check(False, 'EXCEPT(%s)' % _code())
126 return _check(False, 'EXCEPT(%s)' % _code())
128 def wvfailure_count():
131 def _check_unfinished():
133 for func in _registered:
134 print 'WARNING: not run: %r' % (func,)
135 WVFAIL('wvtest_main() not called')
139 atexit.register(_check_unfinished)
142 def _run_in_chdir(path, func, *args, **kwargs):
147 sys.path += [path, os.path.split(path)[0]]
148 return func(*args, **kwargs)
154 if sys.version_info >= (2,6,0):
155 _relpath = os.path.relpath;
157 # Implementation for Python 2.5, taken from CPython (tag v2.6,
158 # file Lib/posixpath.py, hg-commit 95fff5a6a276). Update
159 # ./LICENSE When this code is eventually removed.
160 def _relpath(path, start=os.path.curdir):
162 raise ValueError("no path specified")
164 start_list = os.path.abspath(start).split(os.path.sep)
165 path_list = os.path.abspath(path).split(os.path.sep)
167 # Work out how much of the filepath is shared by start and path.
168 i = len(os.path.commonprefix([start_list, path_list]))
170 rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
173 return os.path.join(*rel_list)
176 def _runtest(fname, f):
177 mod = inspect.getmodule(f)
178 relpath = _relpath(mod.__file__, os.getcwd()).replace('.pyc', '.py')
180 print 'Testing "%s" in %s:' % (fname, relpath)
183 _run_in_chdir(os.path.split(mod.__file__)[0], f)
186 print traceback.format_exc()
187 tb = sys.exc_info()[2]
188 wvtest._result(e, traceback.extract_tb(tb)[1], 'EXCEPTION')
191 def _run_registered_tests():
192 import wvtest as _wvtestmod
193 while _wvtestmod._registered:
194 t = _wvtestmod._registered.pop(0)
195 _runtest(t.func_name, t)
199 def wvtest_main(extra_testfiles=[]):
200 import wvtest as _wvtestmod
201 _run_registered_tests()
202 for modname in extra_testfiles:
203 if not os.path.exists(modname):
204 print 'Skipping: %s' % modname
206 if modname.endswith('.py'):
207 modname = modname[:-3]
208 print 'Importing: %s' % modname
209 path, mod = os.path.split(os.path.abspath(modname))
210 nicename = modname.replace(os.path.sep, '.')
211 while nicename.startswith('.'):
212 nicename = modname[1:]
213 _run_in_chdir(path, __import__, nicename, None, None, [])
214 _run_registered_tests()
216 print 'WvTest: %d tests, %d failures.' % (_wvtestmod._tests,
220 if __name__ == '__main__':
221 import wvtest as _wvtestmod
222 sys.modules['wvtest'] = _wvtestmod
223 sys.modules['wvtest.wvtest'] = _wvtestmod
224 wvtest_main(sys.argv[1:])