diff --git a/changelog.d/fix-benefit-cap-earnings-statutory-pay.fixed.md b/changelog.d/fix-benefit-cap-earnings-statutory-pay.fixed.md new file mode 100644 index 0000000000..c95b55bb01 --- /dev/null +++ b/changelog.d/fix-benefit-cap-earnings-statutory-pay.fixed.md @@ -0,0 +1 @@ +- Stop Hypothesis drawing literal constants from policyengine_uk's own modules in tests, so pull request checks (source tree) and Release test (installed wheel) generate the same property-test examples (#2397). diff --git a/policyengine_uk/tests/conftest.py b/policyengine_uk/tests/conftest.py index e0b262a08c..b7521b14e0 100644 --- a/policyengine_uk/tests/conftest.py +++ b/policyengine_uk/tests/conftest.py @@ -152,8 +152,34 @@ def pytest_collection_modifyitems(config, items): _threshold_before = None +def _ignore_hypothesis_local_constants(): + """Stop Hypothesis drawing literal constants from policyengine_uk's code. + + Hypothesis biases its draws with literals from modules it counts as local, + and it does not count anything under site-packages. Run from the source + tree, policyengine_uk's modules are local; run from the installed wheel + (Release test), they are not. The same derandomized seed then generates + different examples in pull request checks and in Release test, so a + failure can appear only at release (issue #2397). With no local constants, + both draw the same examples. test_hypothesis_environment.py checks this + still takes effect. + """ + from hypothesis.internal.conjecture import providers + + if not hasattr(providers, "_get_local_constants"): + raise RuntimeError( + "hypothesis.internal.conjecture.providers._get_local_constants no " + "longer exists; update _ignore_hypothesis_local_constants for this " + "Hypothesis version." + ) + no_local_constants = providers.Constants() + providers._get_local_constants = lambda: no_local_constants + providers.CONSTANTS_CACHE.cache.clear() + + def pytest_configure(config): global _threshold_before + _ignore_hypothesis_local_constants() _owners_at_start.update(_variable_module_owners()) _threshold_before = gc.get_threshold() gc.set_threshold(GC_FIRST_GENERATION_THRESHOLD, *_threshold_before[1:]) diff --git a/policyengine_uk/tests/test_hypothesis_environment.py b/policyengine_uk/tests/test_hypothesis_environment.py new file mode 100644 index 0000000000..dd382b1ba4 --- /dev/null +++ b/policyengine_uk/tests/test_hypothesis_environment.py @@ -0,0 +1,18 @@ +"""Hypothesis draws the same examples from the source tree and the wheel. + +conftest.py stops Hypothesis drawing literal constants from local modules, +which it collects from the source tree but not from an installed wheel +(issue #2397). This fails if a Hypothesis upgrade bypasses that. +""" + +from hypothesis.internal.conjecture import providers + +# Imported so its modules are loaded, as in any test that uses the model: in +# a source checkout they are local modules with many literal constants. +import policyengine_uk # noqa: F401 + + +def test_hypothesis_draws_no_constants_from_local_modules(): + # Read the pool the way Hypothesis's own provider does. + local_constants = providers.HypothesisProvider(None)._local_constants + assert len(local_constants) == 0