From 36283319ded5642c24d61632b2c69b80f103236e Mon Sep 17 00:00:00 2001 From: "David R. MacIver" Date: Tue, 2 Jul 2019 16:11:29 +0100 Subject: [PATCH 1/4] Remove the ability to run a single shrink pass --- hypothesis-python/RELEASE.rst | 4 + .../internal/conjecture/shrinker.py | 23 +---- .../tests/cover/test_conjecture_engine.py | 98 ++++++------------- 3 files changed, 34 insertions(+), 91 deletions(-) create mode 100644 hypothesis-python/RELEASE.rst diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst new file mode 100644 index 0000000000..5d3609c0ff --- /dev/null +++ b/hypothesis-python/RELEASE.rst @@ -0,0 +1,4 @@ +RELEASE_TYPE: patch + +This release removes some defunct internal functionality that was only being used +for testing. It should have no user visible impact. diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py index 2b3cc53606..0454f7f3bf 100644 --- a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py +++ b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py @@ -106,7 +106,7 @@ def accept(run_step): ) def run(self): - return self.run_shrink_pass(definition.name) + assert False, "Shrink passes should not be run directly" run.__name__ = run_step.__name__ run.is_shrink_pass = True @@ -395,27 +395,6 @@ def debug(self, msg): def random(self): return self.__engine.random - def run_shrink_pass(self, sp): - """Runs the function associated with ShrinkPass sp and updates the - relevant metadata. - - Note that sp may or may not be a pass currently associated with - this shrinker. This does not handle any requeing that is - required. - """ - sp = self.shrink_pass(sp) - - self.debug("Shrink Pass %s" % (sp.name,)) - try: - sp.runs += 1 - - steps = sp.generate_steps() - self.random.shuffle(steps) - for s in steps: - sp.run_step(s) - finally: - self.debug("Shrink Pass %s completed." % (sp.name,)) - def shrink(self): """Run the full set of shrinks and update shrink_target. diff --git a/hypothesis-python/tests/cover/test_conjecture_engine.py b/hypothesis-python/tests/cover/test_conjecture_engine.py index e345a2bd56..bcaf9e9cbb 100644 --- a/hypothesis-python/tests/cover/test_conjecture_engine.py +++ b/hypothesis-python/tests/cover/test_conjecture_engine.py @@ -479,14 +479,18 @@ def shrinker(data): # We normally would have populated this in minimize_individual_blocks shrinker.is_shrinking_block = lambda x: True - fixate(Shrinker.example_deletion_with_block_lowering)(shrinker) + shrinker.fixate_shrink_passes(["example_deletion_with_block_lowering"]) assert list(shrinker.shrink_target.buffer) == [1, 1] def test_deletion_and_lowering_fails_to_shrink(monkeypatch): monkeypatch.setattr( - Shrinker, "shrink", Shrinker.example_deletion_with_block_lowering + Shrinker, + "shrink", + lambda self: self.fixate_shrink_passes( + ["example_deletion_with_block_lowering"] + ), ) # Would normally be added by minimize_individual_blocks, but we skip # that phase in this test. @@ -761,16 +765,6 @@ def x(data): assert x == hbytes(hbytes([1]) * 10) -def fixate(f): - def accept(self): - prev = None - while self.shrink_target is not prev: - prev = self.shrink_target - f(self) - - return accept - - def test_can_remove_discarded_data(): @shrinking_from(hbytes([0] * 10) + hbytes([11])) def shrinker(data): @@ -813,13 +807,6 @@ def shrinker(data): assert shrinker.shrink_target.has_discards -def shrink_pass(name): - def run(self): - self.run_shrink_pass(name) - - return run - - def test_discarding_can_fail(monkeypatch): @shrinking_from(hbytes([1])) def shrinker(data): @@ -936,11 +923,8 @@ def shrink(buffer, *passes): def accept(f): shrinker = shrinking_from(buffer)(f) - prev = None - while shrinker.shrink_target is not prev: - prev = shrinker.shrink_target - for p in passes: - shrinker.run_shrink_pass(p) + shrinker.fixate_shrink_passes(passes) + return list(shrinker.buffer) return accept @@ -954,10 +938,8 @@ def shrinker(data): if abs(m - n) <= 1 and max(m, n) > 0: data.mark_interesting() - # Run minimize_individual_blocks twice so we have both blocks show - # as changed regardless of which order this happens in. - shrinker.minimize_individual_blocks() - shrinker.minimize_individual_blocks() + shrinker.mark_changed(0) + shrinker.mark_changed(1) shrinker.lower_common_block_offset() x = shrinker.shrink_target.buffer @@ -966,7 +948,11 @@ def shrinker(data): def test_handle_empty_draws(monkeypatch): - monkeypatch.setattr(Shrinker, "shrink", Shrinker.adaptive_example_deletion) + monkeypatch.setattr( + Shrinker, + "shrink", + lambda self: self.fixate_shrink_passes(["adaptive_example_deletion"]), + ) @run_to_buffer def x(data): @@ -1005,17 +991,9 @@ def f(data): assert runner.exit_reason == ExitReason.finished -def test_can_reorder_examples(monkeypatch): - monkeypatch.setattr( - ConjectureRunner, - "generate_new_examples", - lambda runner: runner.cached_test_function([1, 0, 1, 1, 0, 1, 0, 0, 0]), - ) - - monkeypatch.setattr(Shrinker, "shrink", Shrinker.reorder_examples) - - @run_to_buffer - def x(data): +def test_can_reorder_examples(): + @shrinking_from([1, 0, 1, 1, 0, 1, 0, 0, 0]) + def shrinker(data): total = 0 for _ in range(5): data.start_example(0) @@ -1025,7 +1003,9 @@ def x(data): if total == 2: data.mark_interesting() - assert list(x) == [0, 0, 0, 1, 0, 1, 1, 0, 1] + shrinker.fixate_shrink_passes(["reorder_examples"]) + + assert list(shrinker.buffer) == [0, 0, 0, 1, 0, 1, 1, 0, 1] def test_permits_but_ignores_raising_order(monkeypatch): @@ -1058,8 +1038,7 @@ def shrinker(data): if n == 4: data.mark_interesting() - for i in range(1, 5): - shrinker.run_shrink_pass(block_program("X" * i)) + shrinker.fixate_shrink_passes([block_program("X" * i) for i in range(1, 5)]) assert list(shrinker.shrink_target.buffer) == [0, 4] * 5 @@ -1128,7 +1107,7 @@ def shrinker(data): if result >= 32768 and cap == 1: data.mark_interesting() - shrinker.minimize_individual_blocks() + shrinker.fixate_shrink_passes(["minimize_individual_blocks"]) assert list(shrinker.shrink_target.buffer) == [1, 1, 0, 1, 0, 0, 1] @@ -1272,7 +1251,7 @@ def shrinker(data): if n == 1: data.mark_interesting() - shrinker.minimize_individual_blocks() + shrinker.fixate_shrink_passes(["minimize_individual_blocks"]) assert list(shrinker.shrink_target.buffer) == [0, 1] @@ -1285,7 +1264,7 @@ def shrinker(data): else: data.draw_bits(8) - shrinker.minimize_individual_blocks() + shrinker.fixate_shrink_passes(["minimize_individual_blocks"]) def test_zero_examples_will_zero_blocks(): @@ -1297,7 +1276,7 @@ def shrinker(data): if n == m == 1: data.mark_interesting() - shrinker.run_shrink_pass("zero_examples") + shrinker.fixate_shrink_passes(["zero_examples"]) assert list(shrinker.shrink_target.buffer) == [1, 0, 1] @@ -1314,7 +1293,7 @@ def shrinker(data): data.draw_bits(16) data.mark_interesting() - shrinker.minimize_individual_blocks() + shrinker.fixate_shrink_passes(["minimize_individual_blocks"]) assert list(shrinker.shrink_target.buffer) == [0, 0, 0] @@ -1360,7 +1339,7 @@ def shrinker(data): if data.draw_bits(8) == 7: data.mark_interesting() - shrinker.run_shrink_pass(block_program("-XX")) + shrinker.fixate_shrink_passes([block_program("-XX")]) assert list(shrinker.shrink_target.buffer) == [1, 7] @@ -1477,7 +1456,7 @@ def shrinker(data): if hbytes(data.buffer) == base_buf: data.mark_interesting() - shrinker.minimize_floats() + shrinker.fixate_shrink_passes(["minimize_floats"]) assert shrinker.shrink_target.buffer == base_buf @@ -1539,25 +1518,6 @@ def shrinker(data): assert shrinker.calls <= 60 -def test_zero_examples_does_not_try_to_adapt_across_different_sizes(): - @shrinking_from([1, 0, 0] * 10 + [1]) - def shrinker(data): - for _ in hrange(10): - data.draw_bits(1) - data.draw_bits(16) - if data.draw_bits(1): - data.mark_interesting() - - initial = shrinker.calls - shrinker.zero_examples() - assert shrinker.shrink_target.buffer == hbytes(30) + hbytes([1]) - - # Tried each of the 1-bit blocks, plus the whole example, plus the final - # single-bit block. Did not try to expand regions into the trivial two-byte - # blocks on each side. - assert shrinker.calls == initial + 12 - - def test_stable_identifiers_match_their_examples(): def tree(data): data.start_example(1) From 186f63e7663bbff1fea47e49d9bd3e1543b617d7 Mon Sep 17 00:00:00 2001 From: "David R. MacIver" Date: Tue, 2 Jul 2019 17:47:47 +0100 Subject: [PATCH 2/4] Don't assign unused variable --- .../src/hypothesis/internal/conjecture/shrinker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py index 0454f7f3bf..da13038671 100644 --- a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py +++ b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py @@ -101,7 +101,7 @@ def defines_shrink_pass(generate_arguments): """A convenient decorator for defining shrink passes.""" def accept(run_step): - definition = ShrinkPassDefinition( + ShrinkPassDefinition( generate_arguments=generate_arguments, run_step=run_step ) From 59191d55103dcaf04e374e001a325949f066ed4b Mon Sep 17 00:00:00 2001 From: "David R. MacIver" Date: Tue, 2 Jul 2019 19:05:08 +0100 Subject: [PATCH 3/4] Formatting --- .../src/hypothesis/internal/conjecture/shrinker.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py index da13038671..0d8134860a 100644 --- a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py +++ b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py @@ -101,9 +101,7 @@ def defines_shrink_pass(generate_arguments): """A convenient decorator for defining shrink passes.""" def accept(run_step): - ShrinkPassDefinition( - generate_arguments=generate_arguments, run_step=run_step - ) + ShrinkPassDefinition(generate_arguments=generate_arguments, run_step=run_step) def run(self): assert False, "Shrink passes should not be run directly" From 0d738f30b29f69d2d555afadcc3149acfc2f620f Mon Sep 17 00:00:00 2001 From: "David R. MacIver" Date: Wed, 3 Jul 2019 08:25:07 +0100 Subject: [PATCH 4/4] Exclude the function that should never be called from coverage --- .../src/hypothesis/internal/conjecture/shrinker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py index 0d8134860a..969c7bed81 100644 --- a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py +++ b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py @@ -103,7 +103,7 @@ def defines_shrink_pass(generate_arguments): def accept(run_step): ShrinkPassDefinition(generate_arguments=generate_arguments, run_step=run_step) - def run(self): + def run(self): # pragma: no cover assert False, "Shrink passes should not be run directly" run.__name__ = run_step.__name__