]> arthur.barton.de Git - bup.git/blob - wvtest.py
do_bloom(): remove unused "count" variable
[bup.git] / wvtest.py
1 #!/usr/bin/env python
2 #
3 # WvTest:
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
8 #
9 import atexit
10 import inspect
11 import os
12 import re
13 import sys
14 import traceback
15
16 # NOTE
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.
29 #
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
33     _registered = []
34     _tests = 0
35     _fails = 0
36
37     def wvtest(func):
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).
42         """
43         _registered.append(func)
44         return func
45
46
47     def _result(msg, tb, code):
48         global _tests, _fails
49         _tests += 1
50         if code != 'ok':
51             _fails += 1
52         (filename, line, func, text) = tb
53         filename = os.path.basename(filename)
54         msg = re.sub(r'\s+', ' ', str(msg))
55         sys.stderr.flush()
56         print '! %-70s %s' % ('%s:%-4d %s' % (filename, line, msg),
57                               code)
58         sys.stdout.flush()
59
60
61     def _check(cond, msg = 'unknown', tb = None):
62         if tb == None: tb = traceback.extract_stack()[-3]
63         if cond:
64             _result(msg, tb, 'ok')
65         else:
66             _result(msg, tb, 'FAILED')
67         return cond
68
69
70     def _code():
71         (filename, line, func, text) = traceback.extract_stack()[-3]
72         text = re.sub(r'^\w+\((.*)\)(\s*#.*)?$', r'\1', text);
73         return text
74
75
76     def WVMSG(message):
77         ''' Issues a notification. '''
78         return _result(message, traceback.extract_stack()[-3], 'ok')
79
80     def WVPASS(cond = True):
81         ''' Counts a test failure unless cond is true. '''
82         return _check(cond, _code())
83
84     def WVFAIL(cond = True):
85         ''' Counts a test failure  unless cond is false. '''
86         return _check(not cond, 'NOT(%s)' % _code())
87
88     def WVPASSEQ(a, b):
89         ''' Counts a test failure unless a == b. '''
90         return _check(a == b, '%s == %s' % (repr(a), repr(b)))
91
92     def WVPASSNE(a, b):
93         ''' Counts a test failure unless a != b. '''
94         return _check(a != b, '%s != %s' % (repr(a), repr(b)))
95
96     def WVPASSLT(a, b):
97         ''' Counts a test failure unless a < b. '''
98         return _check(a < b, '%s < %s' % (repr(a), repr(b)))
99
100     def WVPASSLE(a, b):
101         ''' Counts a test failure unless a <= b. '''
102         return _check(a <= b, '%s <= %s' % (repr(a), repr(b)))
103
104     def WVPASSGT(a, b):
105         ''' Counts a test failure unless a > b. '''
106         return _check(a > b, '%s > %s' % (repr(a), repr(b)))
107
108     def WVPASSGE(a, b):
109         ''' Counts a test failure unless a >= b. '''
110         return _check(a >= b, '%s >= %s' % (repr(a), repr(b)))
111
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.
117         '''
118         try:
119             func(*args, **kwargs)
120         except etype, e:
121             return _check(True, 'EXCEPT(%s)' % _code())
122         except:
123             _check(False, 'EXCEPT(%s)' % _code())
124             raise
125         else:
126             return _check(False, 'EXCEPT(%s)' % _code())
127
128     def wvfailure_count():
129         return _fails
130
131     def _check_unfinished():
132         if _registered:
133             for func in _registered:
134                 print 'WARNING: not run: %r' % (func,)
135             WVFAIL('wvtest_main() not called')
136         if _fails:
137             sys.exit(1)
138
139     atexit.register(_check_unfinished)
140
141
142 def _run_in_chdir(path, func, *args, **kwargs):
143     oldwd = os.getcwd()
144     oldpath = sys.path
145     try:
146         os.chdir(path)
147         sys.path += [path, os.path.split(path)[0]]
148         return func(*args, **kwargs)
149     finally:
150         os.chdir(oldwd)
151         sys.path = oldpath
152
153
154 if sys.version_info >= (2,6,0):
155     _relpath = os.path.relpath;
156 else:
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):
161         if not path:
162             raise ValueError("no path specified")
163
164         start_list = os.path.abspath(start).split(os.path.sep)
165         path_list = os.path.abspath(path).split(os.path.sep)
166
167         # Work out how much of the filepath is shared by start and path.
168         i = len(os.path.commonprefix([start_list, path_list]))
169
170         rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
171         if not rel_list:
172             return curdir
173         return os.path.join(*rel_list)
174
175
176 def _runtest(fname, f):
177     mod = inspect.getmodule(f)
178     relpath = _relpath(mod.__file__, os.getcwd()).replace('.pyc', '.py')
179     print
180     print 'Testing "%s" in %s:' % (fname, relpath)
181     sys.stdout.flush()
182     try:
183         _run_in_chdir(os.path.split(mod.__file__)[0], f)
184     except Exception, e:
185         print
186         print traceback.format_exc()
187         tb = sys.exc_info()[2]
188         wvtest._result(e, traceback.extract_tb(tb)[1], 'EXCEPTION')
189
190
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)
196         print
197
198
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
205             continue
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()
215     print
216     print 'WvTest: %d tests, %d failures.' % (_wvtestmod._tests,
217                                               _wvtestmod._fails)
218
219
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:])