예제 #1
0
    def test_example_coverage(self) -> None:
        # Try to get examples that highlist the differences in the code.
        # Here, we add more conditions for the `return True` path and
        # another case where we used to just `return False`.
        def isack1(s: str) -> bool:
            if s in ("y", "yes"):
                return True
            return False

        def isack2(s: str) -> Optional[bool]:
            if s in ("y", "yes", "Y", "YES"):
                return True
            if s in ("n", "no", "N", "NO"):
                return False
            return None

        diffs = diff_behavior(
            FunctionInfo.from_fn(isack1),
            FunctionInfo.from_fn(isack2),
            DEFAULT_OPTIONS.overlay(max_iterations=20,
                                    per_condition_timeout=5),
        )
        debug("diffs=", diffs)
        assert not isinstance(diffs, str)
        return_vals = set(
            (d.result1.return_repr, d.result2.return_repr) for d in diffs)
        self.assertEqual(return_vals, {("False", "None"), ("False", "True")})
def test_CompositeConditionParser():
    composite = CompositeConditionParser()
    composite.parsers.append(Pep316Parser(composite))
    composite.parsers.append(AssertsParser(composite))
    assert composite.get_fn_conditions(
        FunctionInfo.from_fn(single_line_condition)
    ).has_any()
    assert composite.get_fn_conditions(FunctionInfo.from_fn(avg_with_asserts)).has_any()
 def test_locally_defined_raises_condition(self) -> None:
     conditions = Pep316Parser().get_fn_conditions(
         FunctionInfo.from_fn(locally_defined_raises_condition)
     )
     assert conditions is not None
     self.assertEqual([], list(conditions.syntax_messages()))
     self.assertEqual(set([LocallyDefiendException]), conditions.raises)
 def test_tricky_raises_condition(self) -> None:
     conditions = Pep316Parser().get_fn_conditions(
         FunctionInfo.from_fn(tricky_raises_condition)
     )
     assert conditions is not None
     self.assertEqual([], list(conditions.syntax_messages()))
     self.assertEqual(set([KeyError, OSError]), conditions.raises)
 def test_implies_condition(self):
     conditions = Pep316Parser().get_fn_conditions(
         FunctionInfo.from_fn(implies_condition)
     )
     assert conditions is not None
     # This shouldn't explode (avoid a KeyError on record['override']):
     conditions.post[0].evaluate({"record": {}, "_": 0})
예제 #6
0
 def tests_simple_parse(self) -> None:
     conditions = AssertsParser().get_fn_conditions(
         FunctionInfo.from_fn(avg_with_asserts))
     assert conditions is not None
     conditions.fn([])
     self.assertEqual(conditions.fn([2.2]), 2.2)
     with self.assertRaises(AssertionError):
         conditions.fn([9.2, 17.8])
 def test_single_line_condition(self) -> None:
     conditions = Pep316Parser().get_fn_conditions(
         FunctionInfo.from_fn(single_line_condition)
     )
     assert conditions is not None
     self.assertEqual(
         set([c.expr_source for c in conditions.post]), set(["__return__ >= x"])
     )
def test_CompositeConditionParser_adds_completion_conditions():
    composite_parser = CompositeConditionParser()
    pep316_parser = Pep316Parser(composite_parser)
    composite_parser.parsers.append(pep316_parser)
    fn = FunctionInfo.from_fn(no_postconditions)
    assert len(pep316_parser.get_fn_conditions(fn).pre) == 1
    assert len(pep316_parser.get_fn_conditions(fn).post) == 0
    assert len(composite_parser.get_fn_conditions(fn).post) == 1
    def test_conditions_with_closure_references_and_string_type(self) -> None:
        # This is a function that refers to something in its closure.
        # Ensure we can still look up string-based types:
        def referenced_fn():
            return 4

        def fn_with_closure(foo: "Foo"):
            referenced_fn()

        # Ensure we don't error trying to resolve "Foo":
        Pep316Parser().get_fn_conditions(FunctionInfo.from_fn(fn_with_closure))
예제 #10
0
    def test_diff_behavior_mutation(self) -> None:
        def cut_out_item1(a: List[int], i: int):
            a[i:i + 1] = []

        def cut_out_item2(a: List[int], i: int):
            a[:] = a[:i] + a[i + 1:]

        # TODO: this takes longer than I'd like (few iterations though):
        opts = DEFAULT_OPTIONS.overlay(max_iterations=20,
                                       per_path_timeout=10,
                                       per_condition_timeout=10)
        diffs = diff_behavior(
            FunctionInfo.from_fn(cut_out_item1),
            FunctionInfo.from_fn(cut_out_item2),
            opts,
        )
        assert not isinstance(diffs, str)
        self.assertEqual(len(diffs), 1)
        diff = diffs[0]
        self.assertGreater(len(diff.args["a"]), 1)
        self.assertEqual(diff.args["i"], "-1")
예제 #11
0
    def tests_extra_ast_nodes(self) -> None:
        conditions = AssertsParser().get_fn_conditions(
            FunctionInfo.from_fn(fn_with_docstring_comments_and_assert))
        assert conditions is not None

        # Empty list does not pass precondition, ignored:
        conditions.fn([])

        # normal, passing case:
        nums = [3, 1, 2]
        conditions.fn(nums)
        self.assertEqual(nums, [3, 2])

        # Failing case (duplicate minimum values):
        with self.assertRaises(AssertionError):
            nums = [3, 1, 1, 2]
            conditions.fn(nums)
예제 #12
0
 def _wrap_fn(self,
              fn: Callable,
              conditions: Optional[Conditions] = None) -> Callable:
     wrapper = self.wrapper_map.get(fn)
     if wrapper is not None:
         return wrapper
     if conditions is None:
         conditions = self.condition_parser.get_fn_conditions(
             FunctionInfo.from_fn(fn))  # type: ignore
     if conditions and conditions.has_any():
         wrapper = EnforcementWrapper(self.interceptor(fn), conditions,
                                      self)
         functools.update_wrapper(wrapper, fn)
     else:
         wrapper = fn
     self.wrapper_map[fn] = wrapper
     self.original_map[IdentityWrapper(wrapper)] = fn
     return wrapper
예제 #13
0
        def test_simple_parse(self):
            @icontract.require(lambda l: len(l) > 0)
            @icontract.ensure(lambda l, result: min(l) <= result <= max(l))
            def avg(l):
                return sum(l) / len(l)

            conditions = IcontractParser().get_fn_conditions(FunctionInfo.from_fn(avg))
            assert conditions is not None
            self.assertEqual(len(conditions.pre), 1)
            self.assertEqual(len(conditions.post), 1)
            self.assertEqual(conditions.pre[0].evaluate({"l": []}), False)
            post_args = {
                "l": [42, 43],
                "__old__": AttributeHolder({}),
                "__return__": 40,
                "_": 40,
            }
            self.assertEqual(conditions.post[0].evaluate(post_args), False)
            self.assertEqual(len(post_args), 4)  # (check args are unmodified)
예제 #14
0
from crosshair.core_and_libs import *


def _foo(x: int) -> int:
    if x > 100:
        return 100
    return x


def _regex(x: str) -> bool:
    compiled = re.compile("f(o)+")
    return bool(compiled.fullmatch(x))


OPTS = DEFAULT_OPTIONS.overlay(max_iterations=10, per_condition_timeout=10.0)
foo = FunctionInfo.from_fn(_foo)
regex = FunctionInfo.from_fn(_regex)


def test_path_cover() -> None:
    paths = list(path_cover(foo, OPTS, CoverageType.OPCODE))
    assert len(paths) == 2
    small, large = sorted(paths, key=lambda p: p.result)  # type: ignore
    assert large.result == 100
    assert large.args.arguments["x"] > 100
    assert small.result == small.args.arguments["x"]


def test_path_cover_regex() -> None:
    paths = list(path_cover(regex, OPTS, CoverageType.OPCODE))
    assert len(paths) == 1
예제 #15
0
 def tests_empty_parse(self) -> None:
     conditions = AssertsParser().get_fn_conditions(FunctionInfo.from_fn(debug))
     self.assertEqual(conditions, None)
예제 #16
0
 def test_builtin_conditions_are_null(self) -> None:
     self.assertIsNone(Pep316Parser().get_fn_conditions(FunctionInfo.from_fn(zip)))
예제 #17
0
from crosshair.diff_behavior import diff_behavior
from crosshair.fnutil import walk_qualname
from crosshair.fnutil import FunctionInfo
from crosshair.options import AnalysisOptions
from crosshair.options import DEFAULT_OPTIONS
from crosshair.util import debug
from crosshair.util import set_debug


def _foo1(x: int) -> int:
    if x >= 100:
        return 100
    return x


foo1 = FunctionInfo.from_fn(_foo1)


def _foo2(x: int) -> int:
    return min(x, 100)


foo2 = FunctionInfo.from_fn(_foo2)


def _foo3(x: int) -> int:
    if x > 1000:
        return 1000
    elif x > 100:
        return 100
    else: