deb-python-misaka/tests/chibitest.py
2015-07-10 15:28:31 +02:00

216 lines
5.9 KiB
Python

# -*- coding: utf-8 -*-
"""
chibitest
~~~~~~~~~
chibitest is a simple unit testing module. Less code is less bugs.
Inspired by Oktest, http://www.kuwata-lab.com/oktest/.
"""
from __future__ import print_function
import sys
import inspect
import traceback
from difflib import unified_diff
from collections import namedtuple
Result = namedtuple('Result', ('func', 'name', 'failure'))
def _exc_name(exception_class):
if not inspect.isclass(exception_class):
exception_class = exception_class.__class__
return '<{}.{}>'.format(
exception_class.__module__,
exception_class.__name__)
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('{!r} < {!r}'.format(self._target, other))
def __le__(self, other):
if not self._target <= other:
raise AssertionError('{!r} <= {!r}'.format(self._target, other))
def __eq__(self, other):
if not self._target == other:
raise AssertionError('{!r} == {!r}'.format(self._target, other))
def __ne__(self, other):
if not self._target != other:
raise AssertionError('{!r} != {!r}'.format(self._target, other))
def __gt__(self, other):
if not self._target > other:
raise AssertionError('{!r} > {!r}'.format(self._target, other))
def __ge__(self, other):
if not self._target >= other:
raise AssertionError('{!r} >= {!r}'.format(self._target, other))
def length(self, other):
target_length = len(self._target)
if target_length > other:
raise AssertionError('Higher than desired length: {!r} > {!r}'
.format(target_length, other))
elif target_length < other:
raise AssertionError('Lower than desired length: {!r} < {!r}'
.format(target_length, other))
def diff(self, other):
if self._target != other:
difference = unified_diff(
other.splitlines(True),
self._target.splitlines(True))
raise AssertionError(''.join(difference))
def contains(self, other):
if other not in self._target:
raise AssertionError('{!r} in {!r}'.format(other, self._target))
def not_contains(self, other):
if other in self._target:
raise AssertionError('{!r} not in {!r}'.format(other, self._target))
def raises(self, exception_class=Exception):
name = _exc_name(exception_class)
# ``exception_class`` raised. Good!
# Anything other than ``exception_class`` raised. Wrong!
# No exception. Wrong!
try:
self._target()
except exception_class:
pass
except Exception as e:
raise AssertionError('Expected {}, but got {}:\n{}'
.format(name, _exc_name(e), e))
else:
raise AssertionError('{} not raised'.format(name))
def not_raises(self, exception_class=Exception):
name = _exc_name(exception_class)
# No exception raised. Good!
# ``exception_class`` raised. Wrong!
# Any exception raised. Wrong!
try:
self._target()
except exception_class as e:
raise AssertionError('{} raised:\n{}'.format(name, e))
except Exception as e:
raise AssertionError('Expected {} when failing, but got {}:\n{}'
.format(name, _exc_name(e), e))
# A nicer alias.
ok = AssertionObject
class TestCase(object):
def __init__(self, config):
self.config = config
self._tests = []
for t in dir(self):
if t.startswith('test_'):
self._tests.append(self._wrap_test(getattr(self, t)))
def add_test(self, func):
self._tests.append(self._wrap_test(func))
def _wrap_test(self, func):
def catch_exception():
failure = None
try:
func()
except AssertionError as e:
failure = str(e)
except Exception as e:
failure = ''.join(traceback.format_exception(
*sys.exc_info())).strip()
doc = ''
if func.__doc__:
doc = func.__doc__.lstrip()
idx = doc.find('\n')
if idx > 0:
doc = doc[:idx]
return Result(func.__name__, doc or None, failure)
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, setup_func=None, teardown_func=None, config={}):
line = '*' * 80
passed = failed = 0
config = ExtendedDict(config)
if setup_func:
setup_func()
for testcase in testcases:
tests = testcase(config)
print('>> {}'.format(tests.name if hasattr(tests, 'name')
else testcase.__name__))
for result in tests.run():
name = result.name or result.func
if result.failure is not None:
failed += 1
if result.failure:
print('{} ... FAILED\n{}\n{}\n{}'
.format(name, line, result.failure, line))
else:
print('{} ... FAILED'.format(name))
else:
passed += 1
print('{} ... PASSED'.format(name))
print()
if teardown_func:
teardown_func()
print('{} passed; {} failed.'.format(passed, failed))
if failed > 0:
sys.exit(1)