Source code for dectest.sideaffects

:class:`TestSuite`.
"""

import functools

class SideAffectTest():
    """
[docs] A base class for other side affect tests. """ name = "" def pre_test(self): """
[docs] Called before the function that is being tested is run. An optional callback. """ return def test(self): """
[docs] The actuall test that will be run. Should return `True` for success, and `False` for fail. """ raise NotImplementedError() def decorator(self, *args, **kwargs): """
[docs] Is the decorator that is accessed at the attribute with the same name as the side affect test. As this is a decorator, it must return a function. If only the arguments are going to be used, and the function does not need to be modified, return :method:`SideAffectTest.blank_decorator`. """ return self.blank_decorator @staticmethod def blank_decorator(func):
"""
[docs] A decorator that does nothing. """ return func class GlobalStateChange(SideAffectTest): """
[docs] A side affect test for changes in global state. >>> ts = TestSuite("global state suite", ... DictConfig({'testing': ... {'sideaffects': ['dectest.sideaffects.GlobalStateChange']}})) >>> globalvar = 3 >>> @ts.register("tc") ... @ts.tc.input(1) ... @ts.tc.globalstatechange({'globalvar': (lambda a, b: a + 1 == b)}) ... def add(delta): ... globalvar += delta ... >>> @ts.register("tc2") ... @ts.tc2.input(1) ... @ts.tc2.globalstatechange({'globalvar': 1}) ... def set(i): ... global globalvar ... globalvar = i ... """ name = "globalstatechange" func = None pre_call = {} tests = {} def pre_test(self): """
[docs] Called before the tested function is called, so we use this to capture the global variables' state before the function is called. """ for varname in self.tests: self.pre_call[varname] = self.func.__globals__[varname] def test(self): """
[docs] Returns true if the `self.func.__globals__` is equal to `self.initglobal.update(self.expectedglobal)`. """ glob = self.func.__globals__ for varname, test in self.tests.iteritems(): if callable(test): if not test(self.pre_call[varname], glob[varname]): return False else: if test != glob[varname]: return False return True def decorator(self, tests): """
[docs] Takes a dictionary mapping variable names to tester functions. Each variable given as a key will be retreived before and after the function is run, and both are then passed to the tester function. If the tester function returns `True`, then the test has passed. The dictionary may also have keys that are non callable values. If a value is found that isn't callable, it will just be compared for equality with the value of the associated variable after the tested function has been called. """ self.tests = tests def dec(func): """ Get the function, so as to retrieve the global state later. """ self.func = func return func return dec class ClassStateChange(SideAffectTest): """
[docs] A side affect test for changes in a class' state. >>> ts = TestSuite("class state suite", DictConfig({'testing': {'sideaffects': ['dectest.sideaffects.ClassStateChange']}})) >>> class TestedClass(): ... a = 3 ... ... @ts.register("tc", method=True) ... @ts.tc.in(4) ... @ts.tc.classstatechange({'a': 4}) ... def change_state(self, new_a): ... self.a = new_a ... """ name = "classstatechange" def decorator(self, tests): """
[docs] Takes a dict of items that should be in the class' namespace, and stores them for use by the :class:`ClassStateChange` later. """ self.tests = tests self.f_state = {} self.s_state = {} def dec(func): """ Get the function, to make the class state accessible later. """ # We need to get the states like this as we can't get them from # the func object, as it is unbound, so has no __self__ attribute. @functools.wraps(func) def inner(*args, **kwargs): if not self.f_state: for varname in self.tests: self.f_state[varname] = getattr(args[0], varname) out = func(*args, **kwargs) if not self.s_state: for varname in self.tests: self.s_state[varname] = getattr(args[0], varname) return out return inner return dec def test(self): """
[docs] Checks that the state has changed as determined by the argument passed to the decorator function. """ for varname, tester in self.tests.items(): if callable(tester): if not tester(self.f_state[varname], self.s_state[varname]): return False else: if not tester == self.s_state[varname]: return False return True