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);
76 def WVPASS(cond = True):
77 ''' Counts a test failure unless cond is true. '''
78 return _check(cond, _code())
80 def WVFAIL(cond = True):
81 ''' Counts a test failure unless cond is false. '''
82 return _check(not cond, 'NOT(%s)' % _code())
85 ''' Counts a test failure unless a == b. '''
86 return _check(a == b, '%s == %s' % (repr(a), repr(b)))
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)))
108 def WVEXCEPT(etype, func, *args, **kwargs):
109 ''' Counts a test failure unless func throws an 'etype' exception.
110 You have to spell out the function name and arguments, rather than
111 calling the function yourself, so that WVEXCEPT can run before
112 your test code throws an exception.
115 func(*args, **kwargs)
117 return _check(True, 'EXCEPT(%s)' % _code())
119 _check(False, 'EXCEPT(%s)' % _code())
122 return _check(False, 'EXCEPT(%s)' % _code())
125 def _check_unfinished():
127 for func in _registered:
128 print 'WARNING: not run: %r' % (func,)
129 WVFAIL('wvtest_main() not called')
133 atexit.register(_check_unfinished)
136 def _run_in_chdir(path, func, *args, **kwargs):
141 sys.path += [path, os.path.split(path)[0]]
142 return func(*args, **kwargs)
148 def _runtest(fname, f):
149 mod = inspect.getmodule(f)
150 relpath = os.path.relpath(mod.__file__, os.getcwd()).replace('.pyc', '.py')
152 print 'Testing "%s" in %s:' % (fname, relpath)
155 _run_in_chdir(os.path.split(mod.__file__)[0], f)
158 print traceback.format_exc()
159 tb = sys.exc_info()[2]
160 wvtest._result(e, traceback.extract_tb(tb)[1], 'EXCEPTION')
163 def _run_registered_tests():
164 import wvtest as _wvtestmod
165 while _wvtestmod._registered:
166 t = _wvtestmod._registered.pop(0)
167 _runtest(t.func_name, t)
171 def wvtest_main(extra_testfiles=[]):
172 import wvtest as _wvtestmod
173 _run_registered_tests()
174 for modname in extra_testfiles:
175 if not os.path.exists(modname):
176 print 'Skipping: %s' % modname
178 if modname.endswith('.py'):
179 modname = modname[:-3]
180 print 'Importing: %s' % modname
181 path, mod = os.path.split(os.path.abspath(modname))
182 nicename = modname.replace(os.path.sep, '.')
183 while nicename.startswith('.'):
184 nicename = modname[1:]
185 _run_in_chdir(path, __import__, nicename, None, None, [])
186 _run_registered_tests()
188 print 'WvTest: %d tests, %d failures.' % (_wvtestmod._tests,
192 if __name__ == '__main__':
193 import wvtest as _wvtestmod
194 sys.modules['wvtest'] = _wvtestmod
195 sys.modules['wvtest.wvtest'] = _wvtestmod
196 wvtest_main(sys.argv[1:])