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