]> arthur.barton.de Git - bup.git/blob - wvtest.py
Import new wvtest.py.
[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 WVPASS(cond = True):
77         ''' Counts a test failure unless cond is true. '''
78         return _check(cond, _code())
79
80     def WVFAIL(cond = True):
81         ''' Counts a test failure  unless cond is false. '''
82         return _check(not cond, 'NOT(%s)' % _code())
83
84     def WVPASSEQ(a, b):
85         ''' Counts a test failure unless a == b. '''
86         return _check(a == b, '%s == %s' % (repr(a), repr(b)))
87
88     def WVPASSNE(a, b):
89         ''' Counts a test failure unless a != b. '''
90         return _check(a != b, '%s != %s' % (repr(a), repr(b)))
91
92     def WVPASSLT(a, b):
93         ''' Counts a test failure unless a < b. '''
94         return _check(a < b, '%s < %s' % (repr(a), repr(b)))
95
96     def WVPASSLE(a, b):
97         ''' Counts a test failure unless a <= b. '''
98         return _check(a <= b, '%s <= %s' % (repr(a), repr(b)))
99
100     def WVPASSGT(a, b):
101         ''' Counts a test failure unless a > b. '''
102         return _check(a > b, '%s > %s' % (repr(a), repr(b)))
103
104     def WVPASSGE(a, b):
105         ''' Counts a test failure unless a >= b. '''
106         return _check(a >= b, '%s >= %s' % (repr(a), repr(b)))
107
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.
113         '''
114         try:
115             func(*args, **kwargs)
116         except etype, e:
117             return _check(True, 'EXCEPT(%s)' % _code())
118         except:
119             _check(False, 'EXCEPT(%s)' % _code())
120             raise
121         else:
122             return _check(False, 'EXCEPT(%s)' % _code())
123
124
125     def _check_unfinished():
126         if _registered:
127             for func in _registered:
128                 print 'WARNING: not run: %r' % (func,)
129             WVFAIL('wvtest_main() not called')
130         if _fails:
131             sys.exit(1)
132
133     atexit.register(_check_unfinished)
134
135
136 def _run_in_chdir(path, func, *args, **kwargs):
137     oldwd = os.getcwd()
138     oldpath = sys.path
139     try:
140         os.chdir(path)
141         sys.path += [path, os.path.split(path)[0]]
142         return func(*args, **kwargs)
143     finally:
144         os.chdir(oldwd)
145         sys.path = oldpath
146
147
148 def _runtest(fname, f):
149     mod = inspect.getmodule(f)
150     relpath = os.path.relpath(mod.__file__, os.getcwd()).replace('.pyc', '.py')
151     print
152     print 'Testing "%s" in %s:' % (fname, relpath)
153     sys.stdout.flush()
154     try:
155         _run_in_chdir(os.path.split(mod.__file__)[0], f)
156     except Exception, e:
157         print
158         print traceback.format_exc()
159         tb = sys.exc_info()[2]
160         wvtest._result(e, traceback.extract_tb(tb)[1], 'EXCEPTION')
161
162
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)
168         print
169
170
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
177             continue
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()
187     print
188     print 'WvTest: %d tests, %d failures.' % (_wvtestmod._tests,
189                                               _wvtestmod._fails)
190
191
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:])