Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various test cleanups for Crosshair integration #4090

Merged
merged 5 commits into from
Aug 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions hypothesis-python/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
RELEASE_TYPE: patch

This patch contains some internal code cleanup. There is no user-visible change.
2 changes: 1 addition & 1 deletion hypothesis-python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def local_file(name):
"pytest": ["pytest>=4.6"],
"dpcontracts": ["dpcontracts>=0.4"],
"redis": ["redis>=3.0.0"],
"crosshair": ["hypothesis-crosshair>=0.0.13", "crosshair-tool>=0.0.68"],
"crosshair": ["hypothesis-crosshair>=0.0.13", "crosshair-tool>=0.0.70"],
# zoneinfo is an odd one: every dependency is conditional, because they're
# only necessary on old versions of Python or Windows systems or emscripten.
"zoneinfo": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1935,14 +1935,6 @@ def permitted(f):
}


# eventually we'll want to expose this publicly, but for now it lives as psuedo-internal.
def realize(value: object) -> object:
from hypothesis.control import current_build_context

context = current_build_context()
return context.data.provider.realize(value)


tybug marked this conversation as resolved.
Show resolved Hide resolved
class ConjectureData:
@classmethod
def for_buffer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1112,7 +1112,7 @@ def generate_mutations_from(
(start1, end1), (start2, end2) = self.random.sample(sorted(group), 2)
if (start1 <= start2 <= end2 <= end1) or (
start2 <= start1 <= end1 <= end2
):
): # pragma: no cover # flaky on conjecture-cover tests
# one example entirely contains the other. give up.
# TODO use more intelligent mutation for containment, like
# replacing child with parent or vice versa. Would allow for
Expand Down
6 changes: 1 addition & 5 deletions hypothesis-python/tests/array_api/test_arrays.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,7 @@ def test_minimize_large_uint_arrays(xp, xps):
example."""
if not hasattr(xp, "nonzero"):
pytest.skip("optional API")
smallest = minimal(
xps.arrays(xp.uint8, 100),
lambda x: xp.any(x) and not xp.all(x),
timeout_after=60,
)
smallest = minimal(xps.arrays(xp.uint8, 100), lambda x: xp.any(x) and not xp.all(x))
assert xp.all(xp.logical_or(smallest == 0, smallest == 1))
idx = xp.nonzero(smallest)[0]
assert idx.size in (1, smallest.size - 1)
Expand Down
21 changes: 6 additions & 15 deletions hypothesis-python/tests/common/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,21 @@ class Timeout(BaseException):
pass


def minimal(definition, condition=lambda x: True, settings=None, timeout_after=10):
def minimal(definition, condition=lambda x: True, settings=None):
definition.validate()
runtime = None
result = None

def wrapped_condition(x):
# This sure seems pointless, but `test_sum_of_pair` fails otherwise...
return condition(x)

if (
context := _current_build_context.value
) and context.data.provider.avoid_realization:
raise SkipTest("`minimal()` helper not supported under symbolic execution")

def wrapped_condition(x):
nonlocal runtime
if timeout_after is not None:
if runtime:
runtime += TIME_INCREMENT
if runtime >= timeout_after:
raise Timeout
result = condition(x)
if result and not runtime:
runtime = 0.0
return result

if settings is None:
settings = Settings(max_examples=50000, phases=(Phase.generate, Phase.shrink))
settings = Settings(max_examples=500, phases=(Phase.generate, Phase.shrink))

verbosity = settings.verbosity
if verbosity == Verbosity.normal:
Expand Down
4 changes: 2 additions & 2 deletions hypothesis-python/tests/conjecture/test_alt_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
import pytest

from hypothesis import given, settings, strategies as st
from hypothesis.control import current_build_context
from hypothesis.database import InMemoryExampleDatabase
from hypothesis.errors import Flaky, HypothesisException, InvalidArgument
from hypothesis.internal.compat import int_to_bytes
from hypothesis.internal.conjecture.data import (
AVAILABLE_PROVIDERS,
ConjectureData,
PrimitiveProvider,
realize,
)
from hypothesis.internal.conjecture.engine import ConjectureRunner
from hypothesis.internal.floats import SIGNALING_NAN
Expand Down Expand Up @@ -412,7 +412,7 @@ def test_realize():
@given(st.integers())
@settings(backend="realize")
def test_function(n):
values.append(realize(n))
values.append(current_build_context().data.provider.realize(n))

test_function()

Expand Down
8 changes: 5 additions & 3 deletions hypothesis-python/tests/cover/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,20 @@ def inner(x):


def test_pytest_skip_skips_shrinking():
values = []
seen_large = False

@settings(derandomize=True, max_examples=100)
@given(s.integers())
def inner(x):
values.append(x)
nonlocal seen_large
if x > 100:
if seen_large:
raise Exception("Should never replay a skipped test!")
seen_large = True
pytest.skip(f"{x=} is too big!")

with pytest.raises(Skipped):
inner()
assert len([x for x in values if x > 100]) == 1


def test_can_find_with_db_eq_none():
Expand Down
2 changes: 1 addition & 1 deletion hypothesis-python/tests/cover/test_datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def test_bordering_on_a_leap_year():
dt.datetime.min.replace(year=2003), dt.datetime.max.replace(year=2005)
),
lambda x: x.month == 2 and x.day == 29,
timeout_after=60,
settings=settings(max_examples=1200),
)
assert x.year == 2004

Expand Down
9 changes: 4 additions & 5 deletions hypothesis-python/tests/cover/test_deadline.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_slow_with_none_deadline(i):
def test_raises_flaky_if_a_test_becomes_fast_on_rerun():
once = [True]

@settings(deadline=500)
@settings(deadline=500, backend="hypothesis")
@given(st.integers())
def test_flaky_slow(i):
if once[0]:
Expand Down Expand Up @@ -82,11 +82,10 @@ def test_keeps_you_well_above_the_deadline():
seen = set()
failed_once = [False]

@settings(deadline=100)
@settings(deadline=100, backend="hypothesis")
@given(st.integers(0, 2000))
def slow(i):
# Make sure our initial failure isn't something that immediately goes
# flaky.
# Make sure our initial failure isn't something that immediately goes flaky.
if not failed_once[0]:
if i * 0.9 <= 100:
return
Expand All @@ -107,7 +106,7 @@ def slow(i):
def test_gives_a_deadline_specific_flaky_error_message():
once = [True]

@settings(deadline=100)
@settings(deadline=100, backend="hypothesis")
@given(st.integers())
def slow_once(i):
if once[0]:
Expand Down
42 changes: 21 additions & 21 deletions hypothesis-python/tests/cover/test_health_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
# 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 re
import time

import pytest
from pytest import raises

from hypothesis import HealthCheck, given, settings, strategies as st
from hypothesis.control import assume
from hypothesis.control import assume, current_build_context
from hypothesis.errors import FailedHealthCheck, InvalidArgument
from hypothesis.internal.compat import int_from_bytes
from hypothesis.stateful import (
Expand All @@ -28,26 +28,28 @@

from tests.common.utils import no_shrink

HEALTH_CHECK_SETTINGS = settings(max_examples=11, database=None)
HEALTH_CHECK_SETTINGS = settings(
max_examples=11, database=None, suppress_health_check=()
)


def test_slow_generation_fails_a_health_check():
@HEALTH_CHECK_SETTINGS
@settings(HEALTH_CHECK_SETTINGS, deadline=200)
tybug marked this conversation as resolved.
Show resolved Hide resolved
@given(st.integers().map(lambda x: time.sleep(0.2)))
def test(x):
pass

with raises(FailedHealthCheck):
with pytest.raises(FailedHealthCheck):
test()


def test_slow_generation_inline_fails_a_health_check():
@HEALTH_CHECK_SETTINGS
@settings(HEALTH_CHECK_SETTINGS, deadline=200)
@given(st.data())
def test(data):
data.draw(st.integers().map(lambda x: time.sleep(0.2)))

with raises(FailedHealthCheck):
with pytest.raises(FailedHealthCheck):
test()


Expand Down Expand Up @@ -75,7 +77,7 @@ def unhealthy_filter(x):
def test1(x):
raise ValueError

with raises(FailedHealthCheck):
with pytest.raises(FailedHealthCheck):
test1()

forbidden = set()
Expand All @@ -85,7 +87,7 @@ def test1(x):
def test2(x):
raise ValueError

with raises(ValueError):
with pytest.raises(ValueError):
test2()


Expand All @@ -95,9 +97,8 @@ def test_filtering_everything_fails_a_health_check():
def test(x):
pass

with raises(FailedHealthCheck) as e:
with pytest.raises(FailedHealthCheck, match="filter"):
test()
assert "filter" in e.value.args[0]


class fails_regularly(SearchStrategy):
Expand All @@ -109,21 +110,21 @@ def do_draw(self, data):

def test_filtering_most_things_fails_a_health_check():
@given(fails_regularly())
@settings(database=None, phases=no_shrink)
@settings(database=None, phases=no_shrink, suppress_health_check=())
def test(x):
pass
if current_build_context().data.provider.avoid_realization:
pytest.skip("symbolic backends can filter efficiently!")

Comment on lines +115 to 117
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we instead hardcode a check against the crosshair backend, or only run on the hypothesis backend? avoid_realization is correlated with but not quite the same as a backend filtering efficiently.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm basically assuming that "avoid_realization means symbolic execution", so while it's not the same thing I think it's likely to be the right call.

with raises(FailedHealthCheck) as e:
with pytest.raises(FailedHealthCheck, match="filter"):
test()
assert "filter" in e.value.args[0]


def test_returning_non_none_is_forbidden():
@given(st.integers())
def a(x):
return 1

with raises(FailedHealthCheck):
with pytest.raises(FailedHealthCheck):
a()


Expand Down Expand Up @@ -153,19 +154,18 @@ def test(self, _):

def test_differing_executors_fails_health_check():
sample_test_runner().test()
with pytest.raises(FailedHealthCheck) as exc:
msg = re.escape(str(HealthCheck.differing_executors))
with pytest.raises(FailedHealthCheck, match=msg):
sample_test_runner().test()

assert str(HealthCheck.differing_executors) in str(exc.value)


def test_it_is_an_error_to_suppress_non_iterables():
with raises(InvalidArgument):
with pytest.raises(InvalidArgument):
settings(suppress_health_check=1)


def test_it_is_an_error_to_suppress_non_healthchecks():
with raises(InvalidArgument):
with pytest.raises(InvalidArgument):
settings(suppress_health_check=[1])


Expand Down
3 changes: 3 additions & 0 deletions hypothesis-python/tests/cover/test_targeting.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import pytest

from hypothesis import example, given, strategies as st, target
from hypothesis.control import current_build_context
from hypothesis.errors import InvalidArgument


Expand Down Expand Up @@ -92,6 +93,8 @@ def test_cannot_target_outside_test():

@given(st.none())
def test_cannot_target_same_label_twice(_):
if current_build_context().data.provider.avoid_realization:
pytest.skip("target() is a noop to avoid realizing arguments")
target(0.0, label="label")
with pytest.raises(InvalidArgument):
target(1.0, label="label")
Expand Down
4 changes: 2 additions & 2 deletions hypothesis-python/tests/nocover/test_randomization.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# 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 pytest import raises
import pytest

from hypothesis import Verbosity, core, find, given, settings, strategies as st

Expand All @@ -35,7 +35,7 @@ def test_blah(x, rnd):
def test_nest(y):
assert y < x

with raises(AssertionError):
with pytest.raises(AssertionError):
test_nest()

test_blah()
12 changes: 3 additions & 9 deletions hypothesis-python/tests/nocover/test_recursive.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ def flatten(x):
max_leaves=size * 2,
),
lambda x: isinstance(x, list) and len(flatten(x)) >= size,
timeout_after=None,
)
assert flatten(xs) == [0] * size

Expand All @@ -46,11 +45,7 @@ def depth(x):
else:
return 1

xs = minimal(
st.recursive(st.integers(), st.lists),
lambda x: depth(x) > 1,
timeout_after=None,
)
xs = minimal(st.recursive(st.integers(), st.lists), lambda x: depth(x) > 1)
assert xs in ([0], [[]])


Expand All @@ -67,7 +62,6 @@ def breadth(x):
st.recursive(st.booleans(), lambda x: st.lists(x, max_size=target // 2)),
lambda x: breadth(x) >= target,
settings=settings(max_examples=10000),
timeout_after=None,
)
assert breadth(broad) == target

Expand All @@ -79,7 +73,7 @@ def test_drawing_many_near_boundary():
lambda x: st.lists(x, min_size=2 * (size - 1), max_size=2 * size).map(tuple),
max_leaves=2 * size - 1,
)
ls = minimal(st.lists(elems), lambda x: len(set(x)) >= size, timeout_after=None)
ls = minimal(st.lists(elems), lambda x: len(set(x)) >= size)
assert len(ls) == size


Expand Down Expand Up @@ -117,7 +111,7 @@ def test_can_form_sets_of_recursive_data():
max_leaves=20,
)
)
xs = minimal(trees, lambda x: len(x) >= size, timeout_after=None)
xs = minimal(trees, lambda x: len(x) >= size)
assert len(xs) == size


Expand Down
Loading
Loading