]> arthur.barton.de Git - bup.git/blob - wvtest.py
wvtest.py: set wvtest to wvtest module if __main__
[bup.git] / wvtest.py
1 #!/bin/sh
2 """": # -*-python-*-
3 bup_python="$(dirname "$0")/cmd/bup-python"
4 exec "$bup_python" "$0" ${1+"$@"}
5 """
6 # end of bup preamble
7 #
8 # WvTest:
9 #   Copyright (C)2007-2012 Versabanq Innovations Inc. and contributors.
10 #       Licensed under the GNU Library General Public License, version 2.
11 #       See the included file named LICENSE for license information.
12 #       You can get wvtest from: http://github.com/apenwarr/wvtest
13 #
14 import atexit
15 import inspect
16 import os
17 import re
18 import sys
19 import traceback
20
21 # NOTE
22 # Why do we do we need the "!= main" check?  Because if you run
23 # wvtest.py as a main program and it imports your test files, then
24 # those test files will try to import the wvtest module recursively.
25 # That actually *works* fine, because we don't run this main program
26 # when we're imported as a module.  But you end up with two separate
27 # wvtest modules, the one that gets imported, and the one that's the
28 # main program.  Each of them would have duplicated global variables
29 # (most importantly, wvtest._registered), and so screwy things could
30 # happen.  Thus, we make the main program module *totally* different
31 # from the imported module.  Then we import wvtest (the module) into
32 # wvtest (the main program) here and make sure to refer to the right
33 # versions of global variables.
34 #
35 # All this is done just so that wvtest.py can be a single file that's
36 # easy to import into your own applications.
37 if __name__ != '__main__':   # we're imported as a module
38     _registered = []
39     _tests = 0
40     _fails = 0
41
42     def wvtest(func):
43         """ Use this decorator (@wvtest) in front of any function you want to
44             run as part of the unit test suite.  Then run:
45                 python wvtest.py path/to/yourtest.py [other test.py files...]
46             to run all the @wvtest functions in the given file(s).
47         """
48         _registered.append(func)
49         return func
50
51
52     def _result(msg, tb, code):
53         global _tests, _fails
54         _tests += 1
55         if code != 'ok':
56             _fails += 1
57         (filename, line, func, text) = tb
58         filename = os.path.basename(filename)
59         msg = re.sub(r'\s+', ' ', str(msg))
60         sys.stderr.flush()
61         print '! %-70s %s' % ('%s:%-4d %s' % (filename, line, msg),
62                               code)
63         sys.stdout.flush()
64
65
66     def _check(cond, msg = 'unknown', tb = None):
67         if tb == None: tb = traceback.extract_stack()[-3]
68         if cond:
69             _result(msg, tb, 'ok')
70         else:
71             _result(msg, tb, 'FAILED')
72         return cond
73
74
75     def _code():
76         (filename, line, func, text) = traceback.extract_stack()[-3]
77         text = re.sub(r'^\w+\((.*)\)(\s*#.*)?$', r'\1', text);
78         return text
79
80
81     def WVMSG(message):
82         ''' Issues a notification. '''
83         return _result(message, traceback.extract_stack()[-3], 'ok')
84
85     def WVPASS(cond = True):
86         ''' Counts a test failure unless cond is true. '''
87         return _check(cond, _code())
88
89     def WVFAIL(cond = True):
90         ''' Counts a test failure  unless cond is false. '''
91         return _check(not cond, 'NOT(%s)' % _code())
92
93     def WVPASSEQ(a, b):
94         ''' Counts a test failure unless a == b. '''
95         return _check(a == b, '%s == %s' % (repr(a), repr(b)))
96
97     def WVPASSNE(a, b):
98         ''' Counts a test failure unless a != b. '''
99         return _check(a != b, '%s != %s' % (repr(a), repr(b)))
100
101     def WVPASSLT(a, b):
102         ''' Counts a test failure unless a < b. '''
103         return _check(a < b, '%s < %s' % (repr(a), repr(b)))
104
105     def WVPASSLE(a, b):
106         ''' Counts a test failure unless a <= b. '''
107         return _check(a <= b, '%s <= %s' % (repr(a), repr(b)))
108
109     def WVPASSGT(a, b):
110         ''' Counts a test failure unless a > b. '''
111         return _check(a > b, '%s > %s' % (repr(a), repr(b)))
112
113     def WVPASSGE(a, b):
114         ''' Counts a test failure unless a >= b. '''
115         return _check(a >= b, '%s >= %s' % (repr(a), repr(b)))
116
117     def WVEXCEPT(etype, func, *args, **kwargs):
118         ''' Counts a test failure unless func throws an 'etype' exception.
119             You have to spell out the function name and arguments, rather than
120             calling the function yourself, so that WVEXCEPT can run before
121             your test code throws an exception.
122         '''
123         try:
124             func(*args, **kwargs)
125         except etype, e:
126             return _check(True, 'EXCEPT(%s)' % _code())
127         except:
128             _check(False, 'EXCEPT(%s)' % _code())
129             raise
130         else:
131             return _check(False, 'EXCEPT(%s)' % _code())
132
133     def wvfailure_count():
134         return _fails
135
136     def _check_unfinished():
137         if _registered:
138             for func in _registered:
139                 print 'WARNING: not run: %r' % (func,)
140             WVFAIL('wvtest_main() not called')
141         if _fails:
142             sys.exit(1)
143
144     atexit.register(_check_unfinished)
145
146
147 def _run_in_chdir(path, func, *args, **kwargs):
148     oldwd = os.getcwd()
149     oldpath = sys.path
150     try:
151         os.chdir(path)
152         sys.path += [path, os.path.split(path)[0]]
153         return func(*args, **kwargs)
154     finally:
155         os.chdir(oldwd)
156         sys.path = oldpath
157
158
159 if sys.version_info >= (2,6,0):
160     _relpath = os.path.relpath;
161 else:
162     # Implementation for Python 2.5, taken from CPython (tag v2.6,
163     # file Lib/posixpath.py, hg-commit 95fff5a6a276).  Update
164     # ./LICENSE When this code is eventually removed.
165     def _relpath(path, start=os.path.curdir):
166         if not path:
167             raise ValueError("no path specified")
168
169         start_list = os.path.abspath(start).split(os.path.sep)
170         path_list = os.path.abspath(path).split(os.path.sep)
171
172         # Work out how much of the filepath is shared by start and path.
173         i = len(os.path.commonprefix([start_list, path_list]))
174
175         rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
176         if not rel_list:
177             return curdir
178         return os.path.join(*rel_list)
179
180
181 def _runtest(fname, f):
182     mod = inspect.getmodule(f)
183     relpath = _relpath(mod.__file__, os.getcwd()).replace('.pyc', '.py')
184     print
185     print 'Testing "%s" in %s:' % (fname, relpath)
186     sys.stdout.flush()
187     try:
188         _run_in_chdir(os.path.split(mod.__file__)[0], f)
189     except Exception, e:
190         print
191         print traceback.format_exc()
192         tb = sys.exc_info()[2]
193         wvtest._result(e, traceback.extract_tb(tb)[1], 'EXCEPTION')
194
195
196 def _run_registered_tests():
197     import wvtest as _wvtestmod
198     while _wvtestmod._registered:
199         t = _wvtestmod._registered.pop(0)
200         _runtest(t.func_name, t)
201         print
202
203
204 def wvtest_main(extra_testfiles=[]):
205     import wvtest as _wvtestmod
206     _run_registered_tests()
207     for modname in extra_testfiles:
208         if not os.path.exists(modname):
209             print 'Skipping: %s' % modname
210             continue
211         if modname.endswith('.py'):
212             modname = modname[:-3]
213         print 'Importing: %s' % modname
214         path, mod = os.path.split(os.path.abspath(modname))
215         nicename = modname.replace(os.path.sep, '.')
216         while nicename.startswith('.'):
217             nicename = modname[1:]
218         _run_in_chdir(path, __import__, nicename, None, None, [])
219         _run_registered_tests()
220     print
221     print 'WvTest: %d tests, %d failures.' % (_wvtestmod._tests,
222                                               _wvtestmod._fails)
223
224
225 if __name__ == '__main__':
226     import wvtest as _wvtestmod
227     sys.modules['wvtest'] = _wvtestmod
228     sys.modules['wvtest.wvtest'] = _wvtestmod
229     wvtest = _wvtestmod
230     wvtest_main(sys.argv[1:])