""" open/DurusWorks/sancho/utest.py a lean unit test framework """ import sys, traceback, inspect, pdb, os if sys.version < '3.0': from cStringIO import StringIO else: from io import StringIO class UTest: def __init__(self, debug=False): """ Run each method of self whose name does not start with an underscore as a test. Run self._pre() before each test method. Run self._post() after each test method. Trap all output to stdout, except when an exception is raised in the method being called. If an exception is raised in a method call, print any generated output and a traceback, and proceed to the next method call. If debug is true, don't trap output or exceptions. """ if 'DEBUG' in os.environ: debug = True def run(method, name): """(method:MethodType, name:str) Run method, label output using name. The method runs with stdout redirected to a StringIO. If it runs without raising an exception, no output is generated. If an exception is generated, it prints the accumulated output and a traceback. """ old_stdout = sys.stdout if not debug: sys.stdout = StringIO() try: sys.stdout.write('.-- %s' % name) try: method(self) except KeyboardInterrupt: raise except: data = ''.join(traceback.format_exception(*sys.exc_info())) for line in data.rstrip().split('\n'): sys.stdout.write('|' + line + '\n') if hasattr(sys.stdout, 'getvalue'): sys.__stdout__.write(sys.stdout.getvalue()) else: sys.__stdout__.write( "\nsys.stdout=%r\n" % sys.stdout) if debug: pdb.post_mortem(sys.exc_info()[-1]) finally: sys.stdout = old_stdout names = sorted([name for name in dir(self.__class__) if not name.startswith('_')]) printed = False for name in names: check = getattr(self.__class__, name) if hasattr(check, '__call__'): if not printed: import sys sys.stdout.write("%s: %s:\n" % ( inspect.getsourcefile(check), self.__class__.__name__)) printed = True run(self.__class__._pre, '%s: _pre' % name) run(check, name) run(self.__class__._post, '%s: _post' % name) def _pre(self): """ Executed before each test method. Override as needed. """ def _post(self): """ Executed after each test method. Override as needed. This default removes any attributes whose names do not start with '_' """ for name in list(self.__dict__): if not name.startswith('_'): delattr(self, name) def raises(exc, func, *args, **kwargs): """(exception, func, *args, **kwargs) -> bool Call func and catch the exception. If the exception is not caught, raise an AssertionError """ try: func(*args, **kwargs) except exc: return True raise AssertionError("Expected %s" % exc)