]> arthur.barton.de Git - bup.git/blob - wvtest.py
Merge remote branch 'origin/master' into meta
[bup.git] / wvtest.py
1 #!/usr/bin/env python
2 import traceback
3 import os
4 import re
5 import sys
6
7 if __name__ != "__main__":   # we're imported as a module
8     _registered = []
9     _tests = 0
10     _fails = 0
11
12     def wvtest(func):
13         """ Use this decorator (@wvtest) in front of any function you want to run
14             as part of the unit test suite.  Then run:
15                 python wvtest.py path/to/yourtest.py
16             to run all the @wvtest functions in that file.
17         """
18         _registered.append(func)
19         return func
20
21
22     def _result(msg, tb, code):
23         global _tests, _fails
24         _tests += 1
25         if code != 'ok':
26             _fails += 1
27         (filename, line, func, text) = tb
28         filename = os.path.basename(filename)
29         msg = re.sub(r'\s+', ' ', str(msg))
30         sys.stderr.flush()
31         print '! %-70s %s' % ('%s:%-4d %s' % (filename, line, msg),
32                               code)
33         sys.stdout.flush()
34
35
36     def _check(cond, msg = 'unknown', tb = None):
37         if tb == None: tb = traceback.extract_stack()[-3]
38         if cond:
39             _result(msg, tb, 'ok')
40         else:
41             _result(msg, tb, 'FAILED')
42         return cond
43
44
45     def _code():
46         (filename, line, func, text) = traceback.extract_stack()[-3]
47         text = re.sub(r'^\w+\((.*)\)(\s*#.*)?$', r'\1', text);
48         return text
49
50
51     def WVPASS(cond = True):
52         ''' Counts a test failure unless cond is true. '''
53         return _check(cond, _code())
54
55     def WVFAIL(cond = True):
56         ''' Counts a test failure  unless cond is false. '''
57         return _check(not cond, 'NOT(%s)' % _code())
58
59     def WVPASSEQ(a, b):
60         ''' Counts a test failure unless a == b. '''
61         return _check(a == b, '%s == %s' % (repr(a), repr(b)))
62
63     def WVPASSNE(a, b):
64         ''' Counts a test failure unless a != b. '''
65         return _check(a != b, '%s != %s' % (repr(a), repr(b)))
66
67     def WVPASSLT(a, b):
68         ''' Counts a test failure unless a < b. '''
69         return _check(a < b, '%s < %s' % (repr(a), repr(b)))
70
71     def WVPASSLE(a, b):
72         ''' Counts a test failure unless a <= b. '''
73         return _check(a <= b, '%s <= %s' % (repr(a), repr(b)))
74
75     def WVPASSGT(a, b):
76         ''' Counts a test failure unless a > b. '''
77         return _check(a > b, '%s > %s' % (repr(a), repr(b)))
78
79     def WVPASSGE(a, b):
80         ''' Counts a test failure unless a >= b. '''
81         return _check(a >= b, '%s >= %s' % (repr(a), repr(b)))
82
83     def WVEXCEPT(etype, func, *args, **kwargs):
84         ''' Counts a test failure unless func throws an 'etype' exception.
85             You have to spell out the function name and arguments, rather than
86             calling the function yourself, so that WVEXCEPT can run before
87             your test code throws an exception.
88         '''
89         try:
90             func(*args, **kwargs)
91         except etype, e:
92             return _check(True, 'EXCEPT(%s)' % _code())
93         except:
94             _check(False, 'EXCEPT(%s)' % _code())
95             raise
96         else:
97             return _check(False, 'EXCEPT(%s)' % _code())
98
99 else:  # we're the main program
100     # NOTE
101     # Why do we do this in such a convoluted way?  Because if you run
102     # wvtest.py as a main program and it imports your test files, then
103     # those test files will try to import the wvtest module recursively.
104     # That actually *works* fine, because we don't run this main program
105     # when we're imported as a module.  But you end up with two separate
106     # wvtest modules, the one that gets imported, and the one that's the
107     # main program.  Each of them would have duplicated global variables
108     # (most importantly, wvtest._registered), and so screwy things could
109     # happen.  Thus, we make the main program module *totally* different
110     # from the imported module.  Then we import wvtest (the module) into
111     # wvtest (the main program) here and make sure to refer to the right
112     # versions of global variables.
113     #
114     # All this is done just so that wvtest.py can be a single file that's
115     # easy to import into your own applications.
116     import wvtest
117
118     def _runtest(modname, fname, f):
119         print
120         print 'Testing "%s" in %s.py:' % (fname, modname)
121         sys.stdout.flush()
122         try:
123             f()
124         except Exception, e:
125             print
126             print traceback.format_exc()
127             tb = sys.exc_info()[2]
128             wvtest._result(e, traceback.extract_tb(tb)[1], 'EXCEPTION')
129
130     # main code
131     for modname in sys.argv[1:]:
132         if not os.path.exists(modname):
133             print 'Skipping: %s' % modname
134             continue
135         if modname.endswith('.py'):
136             modname = modname[:-3]
137         print 'Importing: %s' % modname
138         wvtest._registered = []
139         oldwd = os.getcwd()
140         oldpath = sys.path
141         try:
142             path, mod = os.path.split(os.path.abspath(modname))
143             os.chdir(path)
144             sys.path += [path, os.path.split(path)[0]]
145             mod = __import__(modname.replace(os.path.sep, '.'), None, None, [])
146             for t in wvtest._registered:
147                 _runtest(modname, t.func_name, t)
148                 print
149         finally:
150             os.chdir(oldwd)
151             sys.path = oldpath
152
153     print
154     print 'WvTest: %d tests, %d failures.' % (wvtest._tests, wvtest._fails)