Source code for pyquchk.checker

import itertools
import sys
import six
from .arbitraries import Arbitrary


__all__ = ['for_all', 'Result', 'ReturnedFalse', 'CheckError']


[docs]class CheckError(Exception): """ Raised when a test which uses :func:`.qc` decorator fails. It contains the original test data for which the test has failed, and the exception raised by the tested function. Actually, a :exc:`CheckError` instance is always also an instance of the exception raised by the tested function, so you can except it as usually. """ #: Data which caused the function to raise an exception test_data = None #: Exception raised by the function, or :exc:`ReturnedFalse` if function #: returned ``False`` cause = None
def get_CheckError(data, cause): class CheckError_(CheckError, type(cause)): def __init__(self, data, cause): self.test_data = data self.cause = cause def __str__(self): msg = "test failed" if self.cause is not None: msg += " due to %s" % type(self.cause).__name__ cause = str(self.cause) if cause: msg += ": " + cause res = [msg] res.append("Failure encountered for data:") res.extend([" %s = %s" % i for i in sorted(self.test_data.items())]) return '\n'.join(res) CheckError_.__name__ = 'CheckError' return CheckError_(data, cause)
[docs]class ReturnedFalse(Exception): """ Automatically raised when the tested function returns a value which evaluates to a :mod:`bool` ``False``. """ def __init__(self, retval): #: Exact value returned by the function (evaluates to a ``False`` :mod:`bool` value) self.retval = retval
[docs]class Result(object): """ Represent the result of performed testing (returned by :func:`for_all`). """ def __init__(self, verdict, passed, data=None, exception=None, traceback=None): #: ``'OK'`` if passed and ``'FAIL'`` if not self.verdict = verdict #: ``True`` if passed and ``False`` if not (opposite to :attr:`failed`) self.passed = passed #: ``False`` if passed and ``True`` if not (opposite to :attr:`passed`) self.failed = not passed #: Data on which the tested function failed (as a :mod:`dict`), #: or ``None`` if passed self.data = data #: Exception raised by the function, or :exc:`ReturnedFalse` if function #: returned ``False``, or ``None`` if passed self.exception = exception #: Exception traceback, or ``None`` if passed self.traceback = traceback @classmethod def ok(cls): return Result('OK', True) @classmethod def fail(cls, data, exception, traceback): return Result('FAIL', False, data, exception, traceback) def reraise(self): exception = get_CheckError(self.data, self.exception) six.reraise(type(exception), exception, self.traceback)
[docs] def __bool__(self): """ Return :attr:`passed`, used to interprete as a :mod:`bool` value. """ return self.passed
__nonzero__ = __bool__
class Checker(object): def __init__(self, func, args, ntests): self.func = func self.args = args self.ntests = ntests self.arg_arbs = {} for argname, value in self.args.items(): if isinstance(value, type) and not issubclass(value, Arbitrary): # e.g. 'int', 'list' - any class with defined arbitrary_for value = Arbitrary.get_for(value) # Arbitrary instanciated above self.arg_arbs[argname] = value def generate_data(self): each_amount = int(self.ntests ** (1.0 / len(self.arg_arbs))) serial_data = [v.gen_serial(each_amount) for k, v in self.arg_arbs.items()] for values in itertools.product(*serial_data): kwargs = dict(zip(self.arg_arbs.keys(), values)) yield kwargs for _ in range(self.ntests): kwargs = {k: v.next_random() for k, v in self.arg_arbs.items()} yield kwargs def eval_func(self, data): func_res = self.func(**data) if func_res is False: raise ReturnedFalse(func_res) def full_shrink(self, data): changed = True while changed: changed = False shr_gens = [self.arg_arbs[k].shrink(data[k]) for k in self.arg_arbs.keys()] for shr_values in itertools.product(*shr_gens): shr_data = dict(zip(self.arg_arbs.keys(), shr_values)) try: self.eval_func(shr_data) except Exception as e: data = shr_data changed = True return data def for_all(self): for kwargs in self.generate_data(): try: self.eval_func(kwargs) except Exception as e: kwargs = self.full_shrink(kwargs) try: self.eval_func(kwargs) except Exception as e: _, _, traceback = sys.exc_info() return Result.fail(kwargs, e, traceback) return Result.ok()
[docs]def for_all(func, _ntests=100, **kwargs): """ Test the function ``func`` on ``_ntests`` tests. For usage examples see :ref:`property-checking`. :param _ntests: number of tests :param kwargs: function arguments specification :returns: information about the tests run :rtype: :class:`Result` """ return Checker(func, kwargs, _ntests).for_all()

Project Versions