From c2197e2caeda8dd8c2dc3dbe5df1eaa7cd177aa1 Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Tue, 14 Aug 2012 02:31:04 -0400 Subject: [PATCH] python: wvtest scripts can now be executable, like unittest scripts. Now you can either do: ./wvtest.py t/mytest.py or python t/mytest.py If you want to do the latter, you need lines like this at the end of mytest.py: if __name__ == '__main__': wvtest_main() This makes it work more like the python standard unittest library, and makes it really obvious how to run any given test file from the command line, independent from the others. --- python/Makefile | 1 + python/t/__init__.py | 3 + python/t/twvtest.py | 16 +++++ python/t/twvtest2.py | 11 ++++ python/wvtest.py | 143 +++++++++++++++++++++++++++---------------- 5 files changed, 120 insertions(+), 54 deletions(-) create mode 100644 python/t/twvtest2.py diff --git a/python/Makefile b/python/Makefile index e9e23f2..6d54894 100644 --- a/python/Makefile +++ b/python/Makefile @@ -6,6 +6,7 @@ all: runtests: ./wvtest.py \ $(patsubst ./%t,%t/*.py,$(shell find -type d -name t)) + python t/twvtest.py test: ../wvtestrun $(MAKE) runtests diff --git a/python/t/__init__.py b/python/t/__init__.py index e69de29..84815ba 100644 --- a/python/t/__init__.py +++ b/python/t/__init__.py @@ -0,0 +1,3 @@ +import os, sys +parentdir = os.path.join(os.path.abspath(os.path.split(__file__)[0]), '..') +sys.path.append(parentdir) diff --git a/python/t/twvtest.py b/python/t/twvtest.py index f76b254..e5bfbb0 100644 --- a/python/t/twvtest.py +++ b/python/t/twvtest.py @@ -1,10 +1,18 @@ +import __init__ from wvtest import * +import twvtest2 # twvtest2 will also run *before* us since we import it last=None def _except(*args): raise Exception(*args) + +@wvtest +def moretest(): + WVPASSEQ(twvtest2.count, 1) + + @wvtest def test1(): WVPASSLT(1, 2) @@ -36,3 +44,11 @@ def booga1(): @wvtest def chdir_test(): WVPASS(open('testfile.txt')) # will fail if chdir is wrong + + +if __name__ == '__main__': + WVPASSEQ(twvtest2.count, 0) + wvtest_main() + wvtest_main() + WVPASSEQ(last, 'booga1') + WVPASSEQ(twvtest2.count, 1) diff --git a/python/t/twvtest2.py b/python/t/twvtest2.py new file mode 100644 index 0000000..447cd28 --- /dev/null +++ b/python/t/twvtest2.py @@ -0,0 +1,11 @@ +from wvtest import * + + +count = 0 + + +@wvtest +def moretest(): + global count + count += 1 + WVPASS() diff --git a/python/wvtest.py b/python/wvtest.py index d424c55..833cfc4 100755 --- a/python/wvtest.py +++ b/python/wvtest.py @@ -6,21 +6,39 @@ # See the included file named LICENSE for license information. # You can get wvtest from: http://github.com/apenwarr/wvtest # -import traceback +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 @@ -103,59 +121,76 @@ if __name__ != "__main__": # we're imported as a module 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 + + +def _runtest(fname, f): + mod = inspect.getmodule(f) + relpath = os.path.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:]) -- 2.39.2