Use os.path rather than '/' logic in wvtest
[wvtest/kirr.git] / python / wvtest.py
blob86c37a3ccdd18e9ba1d1833592c31a1550220f21
1 #!/usr/bin/env python
2 import traceback
3 import os
4 import re
5 import sys
7 if __name__ != "__main__": # we're imported as a module
8 _registered = []
9 _tests = 0
10 _fails = 0
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
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()
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
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
51 def WVPASS(cond = True):
52 ''' Counts a test failure unless cond is true. '''
53 return _check(cond, _code())
55 def WVFAIL(cond = True):
56 ''' Counts a test failure unless cond is false. '''
57 return _check(not cond, 'NOT(%s)' % _code())
59 def WVPASSEQ(a, b):
60 ''' Counts a test failure unless a == b. '''
61 return _check(a == b, '%s == %s' % (repr(a), repr(b)))
63 def WVPASSNE(a, b):
64 ''' Counts a test failure unless a != b. '''
65 return _check(a != b, '%s != %s' % (repr(a), repr(b)))
67 def WVPASSLT(a, b):
68 ''' Counts a test failure unless a < b. '''
69 return _check(a < b, '%s < %s' % (repr(a), repr(b)))
71 def WVPASSLE(a, b):
72 ''' Counts a test failure unless a <= b. '''
73 return _check(a <= b, '%s <= %s' % (repr(a), repr(b)))
75 def WVPASSGT(a, b):
76 ''' Counts a test failure unless a > b. '''
77 return _check(a > b, '%s > %s' % (repr(a), repr(b)))
79 def WVPASSGE(a, b):
80 ''' Counts a test failure unless a >= b. '''
81 return _check(a >= b, '%s >= %s' % (repr(a), repr(b)))
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())
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.
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
118 def _runtest(modname, fname, f):
119 print
120 print 'Testing "%s" in %s.py:' % (fname, modname)
121 sys.stdout.flush()
122 try:
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')
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
153 print
154 print 'WvTest: %d tests, %d failures.' % (wvtest._tests, wvtest._fails)