119 lines
3.0 KiB
Python
119 lines
3.0 KiB
Python
import re
|
|
import sys
|
|
from difflib import unified_diff
|
|
from collections import namedtuple
|
|
|
|
|
|
RE_TEST_NAME = re.compile(r'test_[a-zA-Z0-9_]*')
|
|
Result = namedtuple('Result', ('func', 'name', 'error'))
|
|
|
|
|
|
def msg(message):
|
|
return '{0}\n{1}\n{0}'.format('-' * 80, message)
|
|
|
|
|
|
class ExtendedDict(dict):
|
|
def __getattr__(self, name):
|
|
if name in self:
|
|
return self[name]
|
|
else:
|
|
return None
|
|
|
|
def __setattr__(self, name, value):
|
|
self[name] = value
|
|
|
|
|
|
class AssertionObject(object):
|
|
def __init__(self, target):
|
|
self._target = target
|
|
|
|
def __lt__(self, other):
|
|
if not self._target < other:
|
|
raise AssertionError(msg('%s < %s' % (self._target, other)))
|
|
|
|
def __le__(self, other):
|
|
if not self._target <= other:
|
|
raise AssertionError(msg('%s <= %s' % (self._target, other)))
|
|
|
|
|
|
def __eq__(self, other):
|
|
if not self._target == other:
|
|
raise AssertionError(msg('%s == %s' % (self._target, other)))
|
|
|
|
def __ne__(self, other):
|
|
if not self._target != other:
|
|
raise AssertionError(msg('%s != %s' % (self._target, other)))
|
|
|
|
def __gt__(self, other):
|
|
if not self._target > other:
|
|
raise AssertionError(msg('%s > %s' % (self._target, other)))
|
|
|
|
def __ge__(self, other):
|
|
if not self._target >= other:
|
|
raise AssertionError(msg('%s >= %s' % (self._target, other)))
|
|
|
|
def diff(self, other):
|
|
if self._target != other:
|
|
difference = unified_diff(
|
|
other.splitlines(True),
|
|
self._target.splitlines(True))
|
|
raise AssertionError(msg(''.join(difference)))
|
|
|
|
|
|
def ok(target):
|
|
return AssertionObject(target)
|
|
|
|
|
|
class TestCase(object):
|
|
def __init__(self, config):
|
|
self.config = config
|
|
self._tests = []
|
|
for t in dir(self):
|
|
if RE_TEST_NAME.match(t):
|
|
self.add_test(getattr(self, t))
|
|
|
|
def add_test(self, func):
|
|
def catch_exception():
|
|
try:
|
|
func()
|
|
error = None
|
|
except AssertionError as e:
|
|
error = str(e)
|
|
return Result(func.__name__, func.__doc__, error)
|
|
self._tests.append(catch_exception)
|
|
return catch_exception
|
|
|
|
def setup(self):
|
|
pass
|
|
|
|
def teardown(self):
|
|
pass
|
|
|
|
def run(self):
|
|
self.setup()
|
|
for test in self._tests:
|
|
yield test()
|
|
self.teardown()
|
|
|
|
|
|
def runner(testcases, config={}):
|
|
passed = failed = 0
|
|
config = ExtendedDict(config)
|
|
|
|
for testcase in testcases:
|
|
tests = testcase(config)
|
|
|
|
if hasattr(tests, 'name'):
|
|
print('\n>> %s' % tests.name)
|
|
|
|
for result in tests.run():
|
|
name = result.name or result.func
|
|
if result.error is not None:
|
|
failed += 1
|
|
print('%s ... FAILED\n\n%s\n' % (name, result.error))
|
|
else:
|
|
passed += 1
|
|
print('%s ... PASSED' % name)
|
|
|
|
print('\n\n%s passed; %s failed.' % (passed, failed))
|