File: test_baseexception.py

package info (click to toggle)
python-hypothesis 6.138.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 15,272 kB
  • sloc: python: 62,853; ruby: 1,107; sh: 253; makefile: 41; javascript: 6
file content (136 lines) | stat: -rw-r--r-- 3,853 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# This file is part of Hypothesis, which may be found at
# https://github.com/HypothesisWorks/hypothesis/
#
# Copyright the Hypothesis Authors.
# Individual contributors are listed in AUTHORS.rst and the git log.
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.

import pytest

from hypothesis import given
from hypothesis.errors import Flaky, FlakyFailure
from hypothesis.strategies import composite, integers, none

from tests.common.utils import Why, skipif_threading, xfail_on_crosshair


@pytest.mark.parametrize(
    "e", [KeyboardInterrupt, SystemExit, GeneratorExit, ValueError]
)
def test_exception_propagates_fine(e):
    @given(integers())
    def test_raise(x):
        raise e

    with pytest.raises(e):
        test_raise()


@pytest.mark.parametrize(
    "e", [KeyboardInterrupt, SystemExit, GeneratorExit, ValueError]
)
def test_exception_propagates_fine_from_strategy(e):
    @composite
    def interrupt_eventually(draw):
        raise e
        # this line will not be executed, but must be here
        # to pass draw function static reference check
        return draw(none())

    @given(interrupt_eventually())
    def test_do_nothing(x):
        pass

    with pytest.raises(e):
        test_do_nothing()


@xfail_on_crosshair(Why.other, strict=False)  # extra replay from backend switch
@pytest.mark.parametrize("e", [KeyboardInterrupt, ValueError])
def test_baseexception_no_rerun_no_flaky(e):
    runs = 0
    interrupt = 3

    @given(integers())
    def test_raise_baseexception(x):
        nonlocal runs
        runs += 1
        if runs == interrupt:
            raise e

    if issubclass(e, (KeyboardInterrupt, SystemExit, GeneratorExit)):
        # Here SystemExit and GeneratorExit are passed through
        with pytest.raises(e):
            test_raise_baseexception()

        assert runs == interrupt
    else:
        with pytest.raises(FlakyFailure):
            test_raise_baseexception()


@xfail_on_crosshair(Why.symbolic_outside_context, strict=False)  # KI and GE only
@pytest.mark.parametrize(
    "e", [KeyboardInterrupt, SystemExit, GeneratorExit, ValueError]
)
def test_baseexception_in_strategy_no_rerun_no_flaky(e):
    runs = 0
    interrupt = 3

    @composite
    def interrupt_eventually(draw):
        nonlocal runs
        runs += 1
        if runs == interrupt:
            raise e
        return draw(integers())

    @given(interrupt_eventually())
    def test_do_nothing(x):
        pass

    if issubclass(e, KeyboardInterrupt):
        with pytest.raises(e):
            test_do_nothing()

        assert runs == interrupt

    else:
        # Now SystemExit and GeneratorExit are caught like other exceptions
        with pytest.raises(Flaky):
            test_do_nothing()


TEMPLATE = """
from hypothesis import given, note, strategies as st

@st.composite
def things(draw):
    raise {exception}
    # this line will not be executed, but must be here
    # to pass draw function static reference check
    return draw(st.none())


@given(st.data(), st.integers())
def test(data, x):
    if x > 100:
        data.draw({strategy})
        raise {exception}
"""


@skipif_threading  # something in pytest here is not thread safe
@pytest.mark.parametrize("exc_name", ["SystemExit", "GeneratorExit"])
@pytest.mark.parametrize("use_composite", [True, False])
def test_explanations(testdir, exc_name, use_composite):
    code = TEMPLATE.format(
        exception=exc_name, strategy="things()" if use_composite else "st.none()"
    )
    test_file = str(testdir.makepyfile(code))
    pytest_stdout = str(testdir.runpytest_inprocess(test_file, "--tb=native").stdout)
    assert "x=101" in pytest_stdout
    assert exc_name in pytest_stdout