Beispiel #1
0
def test_simple_failing_func(decorator: Decorator) -> None:
    """Check we can construct a key, and cache in a dict."""
    # store a ref to the thrown exception outside the function
    # so we can check it's the same one returned
    exception = None

    @decorator
    def f(a: int, b: int) -> int:
        nonlocal exception
        # this exception should be cached by the wrapper
        # so we only see it once
        exception = RuntimeError("failure")

        raise exception

    with Context(dict()) as d:
        try:
            f(1, 2)
        except RuntimeError as e:
            assert e is exception

        with pytest.raises(RuntimeError):
            f(1, 2)

    assert type(d[key(f, 1, 2)]) is Exception
    assert d[key(f, 1, 2)].args[0] is exception
Beispiel #2
0
def test_mock_null_handler(decorator: Decorator) -> None:
    """Check that a null mock handler is called correctly."""
    handler = MagicMock()
    handler.__contains__.return_value = False
    handler.__getitem__.return_value = None
    handler.__setitem__.return_value = None

    @decorator
    def f(a: int, b: int) -> int:
        return a + b

    with Context(handler):
        assert f(1, 2) == 3

    handler.__contains__.assert_called_once_with(key(f, 1, 2))
    handler.__getitem__.assert_not_called()
    handler.__setitem__.assert_called_once_with(key(f, 1, 2), 3)
Beispiel #3
0
def test_fib(decorator: Decorator) -> None:
    """Check that caching fibonacci works."""
    @decorator
    def fib(x: int) -> int:
        if x <= 1:
            return 1
        return fib(x - 1) + fib(x - 2)

    with Context(dict()) as d:
        assert fib(0) == 1
        assert fib(1) == 1
        assert fib(2) == 2
        assert fib(3) == 3

        assert d[key(fib, 0)] == 1
        assert d[key(fib, 1)] == 1
        assert d[key(fib, 2)] == 2
        assert d[key(fib, 3)] == 3
Beispiel #4
0
def test_simple_func(decorator: Decorator, ) -> None:
    """Check we can construct a key, and cache in a dict."""
    @decorator
    def f(a: int, b: int) -> int:
        return a + b

    with Context(dict()) as d:
        f(1, 2)
        f(1, 2)

    assert d[key(f, 1, 2)] == 3
Beispiel #5
0
def test_mock_cached_handler(decorator: Decorator) -> None:
    """Check that a fixed value mock handler is called correctly."""
    return_value = -1
    handler = MagicMock()
    handler.__contains__.return_value = True
    handler.__getitem__.return_value = return_value
    handler.__setitem__.return_value = None

    @decorator
    def f(a: int, b: int) -> int:
        raise AssertionError("this function should not be called")

    with pytest.raises(AssertionError):
        f(1, 2)

    with Context(handler):
        assert f(1, 2) is return_value

    handler.__contains__.assert_called_once_with(key(f, 1, 2))
    handler.__getitem__.assert_called_once_with(key(f, 1, 2))
    handler.__setitem__.assert_not_called()
Beispiel #6
0
def test_simple_graph_bump(print: Callable[..., Any],
                           decorator: Decorator) -> None:
    """Try bumping some functions and check that the graph is consistent."""
    @decorator
    def f(x: int) -> int:
        return x

    @decorator
    def g(x: int, y: int) -> int:
        return f(x) + f(y)

    handler = GraphCallHandler()
    with Context(handler):
        assert g(1, 2) == 3
        assert g(1, 3) == 4

    print(handler.__dict__)

    bumped = handler.bump({key(f, 1): 10})
    with Context(bumped):
        print(bumped.__dict__)

        #
        assert bumped.parents == {key(f, 2): set(), key(f, 3): set()}

        # no nodes have any children left - we evicted them all
        assert not bumped.children
        assert bumped.retvals == {key(f, 2): 2, key(f, 3): 3, key(f, 1): 10}

        assert g(1, 2) == 12

    print(bumped.__dict__)
Beispiel #7
0
def test_simple_graph_exception(decorator: Decorator) -> None:
    """Check we can construct a key, and cache in a dict."""
    # store a ref to the thrown exception outside the function
    # so we can check it's the same one returned
    exception = None

    @decorator
    def f(a: int, b: int) -> int:
        nonlocal exception
        # this exception should be cached by the wrapper
        # so we only see it once
        exception = RuntimeError("failure")

        raise exception

    @decorator
    def g(a: int, b: int) -> int:
        return f(a, b)

    a = 1
    b = 2

    handler = GraphCallHandler()
    with Context(handler):
        try:
            g(a, b)
        except RuntimeError as e:
            assert e is exception

        with pytest.raises(RuntimeError):
            g(a, b)

    # exceptions get cached twice - should this be the case, or do
    # we re-call an throw from source?
    assert type(handler.retvals[key(f, a, b)]) is Exception
    assert type(handler.retvals[key(g, a, b)]) is Exception
    assert handler.retvals[key(f, a, b)].args[0] is exception
    assert handler.retvals[key(g, a, b)].args[0] is exception

    assert handler.parents[key(g, a, b)] == set()
    assert handler.parents[key(f, a, b)] == {key(g, a, b)}
Beispiel #8
0
def test_simple_graph(decorator: Decorator) -> None:
    """Verify we construct an accurate call graph."""
    @decorator
    def f(a: int, b: int) -> int:
        return a + b

    @decorator
    def g(a: int, b: int) -> int:
        return f(a, b)

    a = 1
    b = 2

    handler = GraphCallHandler()
    with Context(handler):
        g(a, b)
        g(a, b)

    assert handler.parents[key(g, a, b)] == set()
    assert handler.parents[key(f, a, b)] == {key(g, a, b)}

    assert handler.children[key(f, a, b)] == set()
    assert handler.children[key(g, a, b)] == {key(f, a, b)}

    assert key(f, a, b) in handler.children

    assert key(f, a, b) in handler.retvals
    assert key(g, a, b) in handler.retvals
    assert handler.retvals[key(f, a, b)] == a + b
    assert handler.retvals[key(g, a, b)] == a + b

    # tweak the cache, check it is used
    handler.retvals[key(g, a, b)] = 123
    with Context(handler):
        assert g(a, b) == 123