File: test_control.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 (235 lines) | stat: -rw-r--r-- 6,432 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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# 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/.

from dataclasses import dataclass

import pytest

from hypothesis import Verbosity, assume, given, reject, reporting, settings
from hypothesis.control import (
    BuildContext,
    _current_build_context,
    _event_to_string,
    cleanup,
    current_build_context,
    currently_in_test_context,
    event,
    note,
)
from hypothesis.errors import (
    HypothesisDeprecationWarning,
    InvalidArgument,
    UnsatisfiedAssumption,
)
from hypothesis.internal.compat import ExceptionGroup
from hypothesis.internal.conjecture.data import ConjectureData
from hypothesis.stateful import RuleBasedStateMachine, rule
from hypothesis.strategies import integers

from tests.common.utils import capture_out


def bc():
    return BuildContext(ConjectureData.for_choices([]), wrapped_test=None)


def test_cannot_cleanup_with_no_context():
    with pytest.raises(InvalidArgument):
        cleanup(lambda: None)
    assert _current_build_context.value is None


def test_cannot_event_with_no_context():
    with pytest.raises(InvalidArgument):
        event("hi")
    assert _current_build_context.value is None


def test_cleanup_executes_on_leaving_build_context():
    data = []
    with bc():
        cleanup(lambda: data.append(1))
        assert not data
    assert data == [1]
    assert _current_build_context.value is None


def test_can_nest_build_context():
    data = []
    with bc():
        cleanup(lambda: data.append(1))
        with bc():
            cleanup(lambda: data.append(2))
            assert not data
        assert data == [2]
    assert data == [2, 1]
    assert _current_build_context.value is None


def test_does_not_suppress_exceptions():
    with pytest.raises(AssertionError):
        # NOTE: For compatibility with Python 3.9's LL(1)
        # parser, this is written as a nested with-statement,
        # instead of a compound one.
        with bc():
            raise AssertionError
    assert _current_build_context.value is None


def test_suppresses_exceptions_in_teardown():
    with pytest.raises(ValueError) as exc_info:
        # NOTE: For compatibility with Python 3.9's LL(1)
        # parser, this is written as a nested with-statement,
        # instead of a compound one.
        with bc():

            def foo():
                raise ValueError

            cleanup(foo)
            raise AssertionError

    assert isinstance(exc_info.value, ValueError)
    assert isinstance(exc_info.value.__cause__, AssertionError)


def test_runs_multiple_cleanup_with_teardown():
    with pytest.raises(ExceptionGroup) as exc_info:
        # NOTE: For compatibility with Python 3.9's LL(1)
        # parser, this is written as a nested with-statement,
        # instead of a compound one.
        with bc():

            def foo():
                raise ValueError

            def bar():
                raise TypeError

            cleanup(foo)
            cleanup(bar)
            raise AssertionError

    assert isinstance(exc_info.value, ExceptionGroup)
    assert isinstance(exc_info.value.__cause__, AssertionError)
    assert {type(e) for e in exc_info.value.exceptions} == {ValueError, TypeError}
    assert _current_build_context.value is None


def test_raises_error_if_cleanup_fails_but_block_does_not():
    with pytest.raises(ValueError):
        # NOTE: For compatibility with Python 3.9's LL(1)
        # parser, this is written as a nested with-statement,
        # instead of a compound one.
        with bc():

            def foo():
                raise ValueError

            cleanup(foo)
    assert _current_build_context.value is None


def test_raises_if_note_out_of_context():
    with pytest.raises(InvalidArgument):
        note("Hi")


def test_deprecation_warning_if_assume_out_of_context():
    with pytest.warns(
        HypothesisDeprecationWarning,
        match="Using `assume` outside a property-based test is deprecated",
    ):
        assume(True)


def test_deprecation_warning_if_reject_out_of_context():
    with pytest.warns(
        HypothesisDeprecationWarning,
        match="Using `reject` outside a property-based test is deprecated",
    ):
        with pytest.raises(UnsatisfiedAssumption):
            reject()


def test_raises_if_current_build_context_out_of_context():
    with pytest.raises(InvalidArgument):
        current_build_context()


def test_current_build_context_is_current():
    with bc() as a:
        assert current_build_context() is a


def test_prints_all_notes_in_verbose_mode():
    # slightly roundabout because @example messes with verbosity - see #1521
    messages = set()

    @settings(verbosity=Verbosity.debug, database=None)
    @given(integers(1, 10))
    def test(x):
        msg = f"x -> {x}"
        note(msg)
        messages.add(msg)
        assert x < 5

    with capture_out() as out:
        # NOTE: For compatibility with Python 3.9's LL(1)
        # parser, this is written as a nested with-statement,
        # instead of a compound one.
        with reporting.with_reporter(reporting.default):
            with pytest.raises(AssertionError):
                test()
    v = out.getvalue()
    for x in sorted(messages):
        assert x in v


@dataclass
class CanBePrettyPrinted:
    n: int


def test_note_pretty_prints():
    reports = []

    @given(integers(1, 10))
    def test(n):
        with reporting.with_reporter(reports.append):
            note(CanBePrettyPrinted(n))
        assert n != 5

    with pytest.raises(AssertionError):
        test()

    assert reports == ["CanBePrettyPrinted(n=5)"]


def test_not_currently_in_hypothesis():
    assert currently_in_test_context() is False


@given(integers())
def test_currently_in_hypothesis(_):
    assert currently_in_test_context() is True


class ContextMachine(RuleBasedStateMachine):
    @rule()
    def step(self):
        assert currently_in_test_context() is True


test_currently_in_stateful_test = ContextMachine.TestCase


def test_can_convert_non_weakref_types_to_event_strings():
    _event_to_string(())