Showing with 117 additions and 47 deletions.
  1. +89 −38 src/hypothesis/core.py
  2. +18 −0 tests/cover/test_core.py
  3. +10 −9 tests/cover/test_testdecorators.py
@@ -24,7 +24,9 @@
import inspect
import binascii
import functools
import sys
import traceback
import types
from random import Random
from itertools import islice
from collections import namedtuple
@@ -100,8 +102,8 @@ def find_satisfying_template(
break
tracker.track(example)
try:
if condition(example):
return example
if (yield condition(example)):
return_value(example)
satisfying_examples += 1
except UnsatisfiedAssumption:
pass
@@ -143,8 +145,8 @@ def find_satisfying_template(
parameter_source.mark_bad()
continue
try:
if condition(example):
return example
if (yield condition(example)):
return_value(example)
except UnsatisfiedAssumption:
parameter_source.mark_bad()
continue
@@ -198,7 +200,6 @@ def simplify_template_such_that(
"""
assert isinstance(random, Random)

yield t
successful_shrinks = 0

changed = True
@@ -231,22 +232,19 @@ def simplify_template_such_that(
for s in simpler:
any_shrinks = True
if time_to_call_it_a_day(settings, start_time):
return
return_value(t)
if tracker.track(s) > 1:
debug_report(
u'Skipping simplifying to duplicate %s' % (
unicode_safe_repr(s),
))
continue
try:
if f(s):
if (yield f(s)):
successful_shrinks += 1
changed = True
yield s
t = s
break
else:
yield t
except UnsatisfiedAssumption:
pass
else:
@@ -260,6 +258,15 @@ def simplify_template_such_that(
unicode_safe_repr(t),
))
break
if not successful_shrinks:
verbose_report(u'Could not shrink example')
elif successful_shrinks == 1:
verbose_report(u'Successfully shrunk example once')
else:
verbose_report(
u'Successfully shrunk example %d times' % (
successful_shrinks,))
return_value(t)


def best_satisfying_template(
@@ -279,27 +286,17 @@ def best_satisfying_template(

successful_shrinks = -1
with settings:
satisfying_example = find_satisfying_template(
satisfying_example = yield find_satisfying_template(
search_strategy, random, condition, tracker, settings, storage,
max_parameter_tries=max_parameter_tries,
)
for simpler in simplify_template_such_that(
satisfying_example = yield simplify_template_such_that(
search_strategy, random, satisfying_example, condition, tracker,
settings, start_time,
):
successful_shrinks += 1
satisfying_example = simpler
)
if storage is not None:
storage.save(satisfying_example, search_strategy)
if not successful_shrinks:
verbose_report(u'Could not shrink example')
elif successful_shrinks == 1:
verbose_report(u'Successfully shrunk example once')
else:
verbose_report(
u'Successfully shrunk example %d times' % (
successful_shrinks,))
return satisfying_example
return_value(satisfying_example)


def test_is_flaky(test, expected_repr):
@@ -384,6 +381,7 @@ def given(*generator_arguments, **generator_kwargs):
# if they were keyword specifiers for data to pass to the test.
provided_random = generator_kwargs.pop(u'random', None)
settings = generator_kwargs.pop(u'settings', None) or Settings.default
driver = generator_kwargs.pop(u'driver', None) or sync_driver

if (provided_random is not None) and settings.derandomize:
raise InvalidArgument(
@@ -464,6 +462,7 @@ def run_test_with_generator(test):
@copy_argspec(
test.__name__, argspec
)
@with_driver(driver)
def wrapped_test(*arguments, **kwargs):
selfy = None
arguments, kwargs = convert_positional_arguments(
@@ -504,7 +503,7 @@ def wrapped_test(*arguments, **kwargs):
test.__name__, arg_string(test, arguments, example_kwargs)
)
try:
test_runner(
yield test_runner(
lambda: test(*arguments, **example_kwargs)
)
except BaseException:
@@ -518,8 +517,8 @@ def wrapped_test(*arguments, **kwargs):
):
# All arguments have been satisfied without needing to invoke
# hypothesis
test_runner(lambda: test(*arguments, **kwargs))
return
result = yield test_runner(lambda: test(*arguments, **kwargs))
return_value(result)

def convert_to_specifier(v):
if isinstance(v, HypothesisProvided):
@@ -547,12 +546,12 @@ def convert_to_specifier(v):
def is_template_example(xs):
record_repr = [None]
try:
test_runner(reify_and_execute(
yield test_runner(reify_and_execute(
search_strategy, xs, test,
always_print=settings.max_shrinks <= 0,
record_repr=record_repr,
))
return False
return_value(False)
except UnsatisfiedAssumption as e:
raise e
except Exception as e:
@@ -561,14 +560,14 @@ def is_template_example(xs):
last_exception[0] = traceback.format_exc()
repr_for_last_exception[0] = record_repr[0]
verbose_report(last_exception[0])
return True
return_value(True)

is_template_example.__name__ = test.__name__
is_template_example.__qualname__ = qualname(test)

falsifying_template = None
try:
falsifying_template = best_satisfying_template(
falsifying_template = yield best_satisfying_template(
search_strategy, random, is_template_example,
settings, storage
)
@@ -578,7 +577,7 @@ def is_template_example(xs):
assert last_exception[0] is not None

with settings:
test_runner(reify_and_execute(
yield test_runner(reify_and_execute(
search_strategy, falsifying_template, test,
print_example=True
))
@@ -588,7 +587,7 @@ def is_template_example(xs):
last_exception[0],
)

test_runner(reify_and_execute(
yield test_runner(reify_and_execute(
search_strategy, falsifying_template,
test_is_flaky(test, repr_for_last_exception[0]),
print_example=True
@@ -604,7 +603,53 @@ def is_template_example(xs):
return run_test_with_generator


def find(specifier, condition, settings=None, random=None, storage=None):
class Return(BaseException):
def __init__(self, value):
self.value = value


def return_value(v):
raise Return(v)


def with_driver(driver):
def decorator(f):
@functools.wraps(f)
def wrapped(*a, **kw):
return driver(f(*a, **kw))
return wrapped
return decorator



def sync_driver(g):
if not isinstance(g, types.GeneratorType):
return g
result = (True, None)
while True:
try:
if result[0]:
result = (True, g.send(result[1]))
else:
result = (True, g.throw(*result[1]))
except Return as e:
result = (True, e.value)
break
except StopIteration:
result = (True, None)
break
else:
try:
result = (True, sync_driver(result[1]))
except BaseException:
result = (False, sys.exc_info())
if result[0]:
return result[1]
else:
raise result[1][0], result[1][1], result[1][2]


def find_task(specifier, condition, settings=None, random=None, storage=None):
settings = settings or Settings(
max_examples=2000,
min_satisfying_examples=0,
@@ -626,7 +671,7 @@ def find(specifier, condition, settings=None, random=None, storage=None):
def template_condition(template):
with BuildContext():
result = search.reify(template)
success = condition(result)
success = yield condition(result)

if success:
successful_examples[0] += 1
@@ -644,19 +689,19 @@ def template_condition(template):
verbose_report(lambda: u'Shrunk example to %s' % (
repr(result),
))
return success
return_value(success)

template_condition.__name__ = condition.__name__
tracker = Tracker()

try:
template = best_satisfying_template(
template = yield best_satisfying_template(
search, random, template_condition, settings,
tracker=tracker, max_parameter_tries=2,
storage=storage,
)
with BuildContext():
return search.reify(template)
return_value(search.reify(template))
except Timeout:
raise
except NoSuchExample:
@@ -668,4 +713,10 @@ def template_condition(template):
raise NoSuchExample(get_pretty_function_description(condition))


def find(specifier, condition, settings=None, random=None, storage=None, driver=None):
if driver is None:
driver = sync_driver
return driver(find_task(specifier, condition, settings=settings, random=random))


load_entry_points()
@@ -23,6 +23,7 @@
from hypothesis import find, given, assume, Settings
from hypothesis.errors import NoSuchExample, Unsatisfiable
from hypothesis.internal.tracker import Tracker
from hypothesis.core import sync_driver, return_value


def test_stops_after_max_examples_if_satisfying():
@@ -91,3 +92,20 @@ def test_settings_are_default_in_find():
find(
s.booleans(), lambda x: Settings.default is some_normal_settings,
settings=some_normal_settings)


def test_simple_driver():
def simple_task():
yield return_value(42)
assert sync_driver(simple_task()) == 42


def test_chained_driver():
def simple_task():
yield return_value(42)
def chained_task():
yield simple_task()
def chained_task2():
assert (yield simple_task()) == 42
yield return_value(56)
assert sync_driver(chained_task2()) == 56
@@ -274,12 +274,12 @@ def test_removing_an_element_from_a_non_unique_list(xs, y):


def test_errors_even_if_does_not_error_on_final_call():
seen = [False]
@given(integers())
def rude(x):
assert not any(
t[3] == u'best_satisfying_template'
for t in inspect.getouterframes(inspect.currentframe())
)
f = seen[0]
seen[0] = True
assert f

with raises(Flaky):
rude()
@@ -294,12 +294,12 @@ def __repr__(self):


def test_reports_repr_diff_in_flaky_error():
seen = [False]
@given(builds(DifferentReprEachTime))
def rude(x):
assert not any(
t[3] == u'best_satisfying_template'
for t in inspect.getouterframes(inspect.currentframe())
)
f = seen[0]
seen[0] = True
assert f

with raises(Flaky) as e:
rude()
@@ -353,7 +353,8 @@ def test_is_an_int(x):
test_is_an_int()
out = out.getvalue()
lines = [l.strip() for l in out.split(u'\n')]
assert all(not l for l in lines)
for l in lines:
assert not l


@given(sampled_from([1]))