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
|
# 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 sys
import typing
import pytest
from hypothesis import HealthCheck, assume, given, settings, strategies as st
from hypothesis.errors import (
HypothesisDeprecationWarning,
HypothesisWarning,
InvalidArgument,
)
from tests.common.debug import minimal
from tests.common.utils import flaky
@st.composite
def badly_draw_lists(draw, m=0):
length = draw(st.integers(m, m + 10))
return [draw(st.integers()) for _ in range(length)]
def test_simplify_draws():
assert minimal(badly_draw_lists(), lambda x: len(x) >= 3) == [0] * 3
def test_can_pass_through_arguments():
assert minimal(badly_draw_lists(5)) == [0] * 5
assert minimal(badly_draw_lists(m=6)) == [0] * 6
@st.composite
def draw_ordered_with_assume(draw):
x = draw(st.floats())
y = draw(st.floats())
assume(x < y)
return (x, y)
@given(draw_ordered_with_assume())
@settings(suppress_health_check=[HealthCheck.filter_too_much])
def test_can_assume_in_draw(xy):
assert xy[0] < xy[1]
def test_uses_definitions_for_reprs():
assert repr(badly_draw_lists()) == "badly_draw_lists()"
assert repr(badly_draw_lists(1)) == "badly_draw_lists(m=1)"
assert repr(badly_draw_lists(m=1)) == "badly_draw_lists(m=1)"
def test_errors_given_default_for_draw():
with pytest.raises(InvalidArgument):
@st.composite
def foo(x=None):
pass
def test_errors_given_function_of_no_arguments():
with pytest.raises(InvalidArgument):
@st.composite
def foo():
pass
def test_errors_given_kwargs_only():
with pytest.raises(InvalidArgument):
@st.composite
def foo(**kwargs):
pass
def test_warning_given_no_drawfn_call():
with pytest.warns(HypothesisDeprecationWarning):
@st.composite
def foo(_):
return "bar"
def test_can_use_pure_args():
@st.composite
def stuff(*args):
return args[0](st.sampled_from(args[1:]))
assert minimal(stuff(1, 2, 3, 4, 5)) == 1
def test_composite_of_lists():
@st.composite
def f(draw):
return draw(st.integers()) + draw(st.integers())
assert minimal(st.lists(f()), lambda x: len(x) >= 10) == [0] * 10
@flaky(min_passes=2, max_runs=5)
def test_can_shrink_matrices_with_length_param():
@st.composite
def matrix(draw):
rows = draw(st.integers(1, 10))
columns = draw(st.integers(1, 10))
return [
[draw(st.integers(0, 10000)) for _ in range(columns)] for _ in range(rows)
]
def transpose(m):
return [[row[i] for row in m] for i in range(len(m[0]))]
def is_square(m):
return len(m) == len(m[0])
value = minimal(matrix(), lambda m: is_square(m) and transpose(m) != m)
assert len(value) == 2
assert len(value[0]) == 2
assert sorted(value[0] + value[1]) == [0, 0, 0, 1]
class MyList(list):
pass
@given(st.data(), st.lists(st.integers()).map(MyList))
def test_does_not_change_arguments(data, ls):
# regression test for issue #1017 or other argument mutation
@st.composite
def strat(draw, arg):
draw(st.none())
return arg
ex = data.draw(strat(ls))
assert ex is ls
class ClsWithStrategyMethods:
@classmethod
@st.composite
def st_classmethod_then_composite(draw, cls):
return draw(st.integers(0, 10))
@st.composite
@classmethod
def st_composite_then_classmethod(draw, cls):
return draw(st.integers(0, 10))
@staticmethod
@st.composite
def st_staticmethod_then_composite(draw):
return draw(st.integers(0, 10))
@st.composite
@staticmethod
def st_composite_then_staticmethod(draw):
return draw(st.integers(0, 10))
@st.composite
def st_composite_method(draw, self):
return draw(st.integers(0, 10))
@given(st.data())
def test_applying_composite_decorator_to_methods(data):
instance = ClsWithStrategyMethods()
for strategy in [
ClsWithStrategyMethods.st_classmethod_then_composite(),
ClsWithStrategyMethods.st_composite_then_classmethod(),
ClsWithStrategyMethods.st_staticmethod_then_composite(),
ClsWithStrategyMethods.st_composite_then_staticmethod(),
instance.st_classmethod_then_composite(),
instance.st_composite_then_classmethod(),
instance.st_staticmethod_then_composite(),
instance.st_composite_then_staticmethod(),
instance.st_composite_method(),
]:
x = data.draw(strategy)
assert isinstance(x, int)
assert 0 <= x <= 10
def test_drawfn_cannot_be_instantiated():
with pytest.raises(TypeError):
st.DrawFn()
@pytest.mark.skipif(sys.version_info[:2] == (3, 9), reason="stack depth varies???")
def test_warns_on_strategy_annotation():
# TODO: print the stack on Python 3.10 and 3.11 to determine the appropriate
# stack depth to use. Consider adding a debug-print if IN_COVERAGE_TESTS
# and the relevant depth is_hypothesis_file(), for easier future fixing.
#
# Meanwhile, the test is not skipped on 3.10/3.11 as it is still required for
# coverage of the warning-generating branch.
with pytest.warns(HypothesisWarning, match="Return-type annotation") as w:
@st.composite
def my_integers(draw: st.DrawFn) -> st.SearchStrategy[int]:
return draw(st.integers())
if sys.version_info[:2] > (3, 11): # TEMP: see PR #3961
assert len(w.list) == 1
assert w.list[0].filename == __file__ # check stacklevel points to user code
def test_composite_allows_overload_without_draw():
# See https://github.com/HypothesisWorks/hypothesis/issues/3970
@st.composite
@typing.overload
def overloaded(draw: st.DrawFn, *, x: int) -> typing.Literal[True]: ...
@st.composite
@typing.overload
def overloaded(draw: st.DrawFn, *, x: str) -> typing.Literal[False]: ...
@st.composite
def overloaded(draw: st.DrawFn, *, x: typing.Union[int, str]) -> bool:
return draw(st.just(isinstance(x, int)))
|