diff --git a/hypothesis-python/tests/cover/test_random_module.py b/hypothesis-python/tests/cover/test_random_module.py index 71f55b9191..aa8b36931a 100644 --- a/hypothesis-python/tests/cover/test_random_module.py +++ b/hypothesis-python/tests/cover/test_random_module.py @@ -13,6 +13,7 @@ # # END HEADER +import gc import random import pytest @@ -20,11 +21,21 @@ from hypothesis import find, given, register_random, reporting, strategies as st from hypothesis.errors import InvalidArgument from hypothesis.internal import entropy +from hypothesis.internal.compat import PYPY from hypothesis.internal.entropy import deterministic_PRNG from tests.common.utils import capture_out +def gc_on_pypy(): + # CPython uses reference counting, so objects (without circular refs) + # are collected immediately on `del`, breaking weak references. + # PyPy doesn't, so we use this function in tests before counting the + # surviving references to ensure that they're deterministic. + if PYPY: + gc.collect() + + def test_can_seed_random(): with capture_out() as out: with reporting.with_reporter(reporting.default): @@ -54,12 +65,14 @@ def test_cannot_register_non_Random(): def test_registering_a_Random_is_idempotent(): + gc_on_pypy() n_registered = len(entropy.RANDOMS_TO_MANAGE) r = random.Random() register_random(r) register_random(r) assert len(entropy.RANDOMS_TO_MANAGE) == n_registered + 1 del r + gc_on_pypy() assert len(entropy.RANDOMS_TO_MANAGE) == n_registered @@ -136,6 +149,7 @@ def test_find_does_not_pollute_state(): def test_evil_prng_registration_nonsense(): + gc_on_pypy() n_registered = len(entropy.RANDOMS_TO_MANAGE) r1, r2, r3 = random.Random(1), random.Random(2), random.Random(3) s2 = r2.getstate() @@ -146,16 +160,19 @@ def test_evil_prng_registration_nonsense(): register_random(r1) k = max(entropy.RANDOMS_TO_MANAGE) # get a handle to check if r1 still exists register_random(r2) + assert len(entropy.RANDOMS_TO_MANAGE) == n_registered + 2 + with deterministic_PRNG(0): del r1 - r2.seed(4) + gc_on_pypy() + assert k not in entropy.RANDOMS_TO_MANAGE, "r1 has been garbage-collected" assert len(entropy.RANDOMS_TO_MANAGE) == n_registered + 1 + r2.seed(4) register_random(r3) r3.seed(4) s4 = r3.getstate() # Implicit check, no exception was raised in __exit__ - assert k not in entropy.RANDOMS_TO_MANAGE, "r1 has been garbage-collected" assert r2.getstate() == s2, "reset previously registered random state" assert r3.getstate() == s4, "retained state when registered within the context"