#!/usr/bin/env python
-import traceback
+#
+# WvTest:
+# Copyright (C)2007-2012 Versabanq Innovations Inc. and contributors.
+# Licensed under the GNU Library General Public License, version 2.
+# See the included file named LICENSE for license information.
+# You can get wvtest from: http://github.com/apenwarr/wvtest
+#
+import atexit
+import inspect
import os
import re
import sys
+import traceback
-if __name__ != "__main__": # we're imported as a module
+# NOTE
+# Why do we do we need the "!= main" check? Because if you run
+# wvtest.py as a main program and it imports your test files, then
+# those test files will try to import the wvtest module recursively.
+# That actually *works* fine, because we don't run this main program
+# when we're imported as a module. But you end up with two separate
+# wvtest modules, the one that gets imported, and the one that's the
+# main program. Each of them would have duplicated global variables
+# (most importantly, wvtest._registered), and so screwy things could
+# happen. Thus, we make the main program module *totally* different
+# from the imported module. Then we import wvtest (the module) into
+# wvtest (the main program) here and make sure to refer to the right
+# versions of global variables.
+#
+# All this is done just so that wvtest.py can be a single file that's
+# easy to import into your own applications.
+if __name__ != '__main__': # we're imported as a module
_registered = []
_tests = 0
_fails = 0
def wvtest(func):
- """ Use this decorator (@wvtest) in front of any function you want to run
- as part of the unit test suite. Then run:
- python wvtest.py path/to/yourtest.py
- to run all the @wvtest functions in that file.
+ """ Use this decorator (@wvtest) in front of any function you want to
+ run as part of the unit test suite. Then run:
+ python wvtest.py path/to/yourtest.py [other test.py files...]
+ to run all the @wvtest functions in the given file(s).
"""
_registered.append(func)
return func
return text
+ def WVMSG(message):
+ ''' Issues a notification. '''
+ return _result(message, traceback.extract_stack()[-3], 'ok')
+
def WVPASS(cond = True):
''' Counts a test failure unless cond is true. '''
return _check(cond, _code())
else:
return _check(False, 'EXCEPT(%s)' % _code())
-else: # we're the main program
- # NOTE
- # Why do we do this in such a convoluted way? Because if you run
- # wvtest.py as a main program and it imports your test files, then
- # those test files will try to import the wvtest module recursively.
- # That actually *works* fine, because we don't run this main program
- # when we're imported as a module. But you end up with two separate
- # wvtest modules, the one that gets imported, and the one that's the
- # main program. Each of them would have duplicated global variables
- # (most importantly, wvtest._registered), and so screwy things could
- # happen. Thus, we make the main program module *totally* different
- # from the imported module. Then we import wvtest (the module) into
- # wvtest (the main program) here and make sure to refer to the right
- # versions of global variables.
- #
- # All this is done just so that wvtest.py can be a single file that's
- # easy to import into your own applications.
- import wvtest
-
- def _runtest(modname, fname, f):
+
+ def _check_unfinished():
+ if _registered:
+ for func in _registered:
+ print 'WARNING: not run: %r' % (func,)
+ WVFAIL('wvtest_main() not called')
+ if _fails:
+ sys.exit(1)
+
+ atexit.register(_check_unfinished)
+
+
+def _run_in_chdir(path, func, *args, **kwargs):
+ oldwd = os.getcwd()
+ oldpath = sys.path
+ try:
+ os.chdir(path)
+ sys.path += [path, os.path.split(path)[0]]
+ return func(*args, **kwargs)
+ finally:
+ os.chdir(oldwd)
+ sys.path = oldpath
+
+
+if sys.version_info >= (2,6,0):
+ _relpath = os.path.relpath;
+else:
+ # Implementation for Python 2.5, taken from CPython (tag v2.6,
+ # file Lib/posixpath.py, hg-commit 95fff5a6a276). Update
+ # ./LICENSE When this code is eventually removed.
+ def _relpath(path, start=os.path.curdir):
+ if not path:
+ raise ValueError("no path specified")
+
+ start_list = os.path.abspath(start).split(os.path.sep)
+ path_list = os.path.abspath(path).split(os.path.sep)
+
+ # Work out how much of the filepath is shared by start and path.
+ i = len(os.path.commonprefix([start_list, path_list]))
+
+ rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
+ if not rel_list:
+ return curdir
+ return os.path.join(*rel_list)
+
+
+def _runtest(fname, f):
+ mod = inspect.getmodule(f)
+ relpath = _relpath(mod.__file__, os.getcwd()).replace('.pyc', '.py')
+ print
+ print 'Testing "%s" in %s:' % (fname, relpath)
+ sys.stdout.flush()
+ try:
+ _run_in_chdir(os.path.split(mod.__file__)[0], f)
+ except Exception, e:
print
- print 'Testing "%s" in %s.py:' % (fname, modname)
- sys.stdout.flush()
- try:
- f()
- except Exception, e:
- print
- print traceback.format_exc()
- tb = sys.exc_info()[2]
- wvtest._result(e, traceback.extract_tb(tb)[1], 'EXCEPTION')
-
- # main code
- for modname in sys.argv[1:]:
+ print traceback.format_exc()
+ tb = sys.exc_info()[2]
+ wvtest._result(e, traceback.extract_tb(tb)[1], 'EXCEPTION')
+
+
+def _run_registered_tests():
+ import wvtest as _wvtestmod
+ while _wvtestmod._registered:
+ t = _wvtestmod._registered.pop(0)
+ _runtest(t.func_name, t)
+ print
+
+
+def wvtest_main(extra_testfiles=[]):
+ import wvtest as _wvtestmod
+ _run_registered_tests()
+ for modname in extra_testfiles:
if not os.path.exists(modname):
print 'Skipping: %s' % modname
continue
if modname.endswith('.py'):
modname = modname[:-3]
print 'Importing: %s' % modname
- wvtest._registered = []
- oldwd = os.getcwd()
- oldpath = sys.path
- try:
- path, mod = os.path.split(os.path.abspath(modname))
- os.chdir(path)
- sys.path += [path, os.path.split(path)[0]]
- mod = __import__(modname.replace(os.path.sep, '.'), None, None, [])
- for t in wvtest._registered:
- _runtest(modname, t.func_name, t)
- print
- finally:
- os.chdir(oldwd)
- sys.path = oldpath
-
+ path, mod = os.path.split(os.path.abspath(modname))
+ nicename = modname.replace(os.path.sep, '.')
+ while nicename.startswith('.'):
+ nicename = modname[1:]
+ _run_in_chdir(path, __import__, nicename, None, None, [])
+ _run_registered_tests()
print
- print 'WvTest: %d tests, %d failures.' % (wvtest._tests, wvtest._fails)
+ print 'WvTest: %d tests, %d failures.' % (_wvtestmod._tests,
+ _wvtestmod._fails)
+
+
+if __name__ == '__main__':
+ import wvtest as _wvtestmod
+ sys.modules['wvtest'] = _wvtestmod
+ sys.modules['wvtest.wvtest'] = _wvtestmod
+ wvtest_main(sys.argv[1:])