-
Notifications
You must be signed in to change notification settings - Fork 590
/
Copy pathconftest.py
125 lines (98 loc) · 4.4 KB
/
conftest.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# This file is part of Hypothesis, which may be found at
# https://github.com/HypothesisWorks/hypothesis/
#
# Copyright the Hypothesis Authors.
# Individual contributors are listed in AUTHORS.rst and the git log.
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# 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 gc
import random
import sys
import time as time_module
import pytest
from hypothesis._settings import is_in_ci
from hypothesis.internal.detection import is_hypothesis_test
from tests.common import TIME_INCREMENT
from tests.common.setup import run
run()
# Skip collection of tests which require the Django test runner,
# or that don't work on the current version of Python.
collect_ignore_glob = ["django/*"]
if sys.version_info < (3, 8):
collect_ignore_glob.append("array_api")
collect_ignore_glob.append("cover/*py38*")
if sys.version_info < (3, 9):
collect_ignore_glob.append("cover/*py39*")
if sys.version_info < (3, 10):
collect_ignore_glob.append("cover/*py310*")
if sys.version_info >= (3, 11):
collect_ignore_glob.append("cover/test_asyncio.py") # @asyncio.coroutine removed
assert sys.version_info.releaselevel == "alpha"
# TODO: our traceback elision doesn't work with Python 3.11's nice new format yet
collect_ignore_glob.append("cover/test_traceback_elision.py")
collect_ignore_glob.append("pytest/test_capture.py")
def pytest_configure(config):
config.addinivalue_line("markers", "slow: pandas expects this marker to exist.")
def pytest_addoption(parser):
parser.addoption("--hypothesis-update-outputs", action="store_true")
parser.addoption("--hypothesis-learn-to-normalize", action="store_true")
@pytest.fixture(scope="function", autouse=True)
def gc_before_each_test():
gc.collect()
@pytest.fixture(scope="function", autouse=True)
def consistently_increment_time(monkeypatch):
"""Rather than rely on real system time we monkey patch time.time so that
it passes at a consistent rate between calls.
The reason for this is that when these tests run in CI, their performance is
extremely variable and the VM the tests are on might go to sleep for a bit,
introducing arbitrary delays. This can cause a number of tests to fail
flakily.
Replacing time with a fake version under our control avoids this problem.
"""
frozen = [False]
current_time = [time_module.time()]
def time():
if not frozen[0]:
current_time[0] += TIME_INCREMENT
return current_time[0]
def sleep(naptime):
current_time[0] += naptime
def freeze():
frozen[0] = True
monkeypatch.setattr(time_module, "time", time)
monkeypatch.setattr(time_module, "monotonic", time)
monkeypatch.setattr(time_module, "perf_counter", time)
monkeypatch.setattr(time_module, "sleep", sleep)
monkeypatch.setattr(time_module, "freeze", freeze, raising=False)
random_states_after_tests = {}
independent_random = random.Random()
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item):
# This hookwrapper checks for PRNG state leaks from Hypothesis tests.
# See: https://github.com/HypothesisWorks/hypothesis/issues/1919
if not (hasattr(item, "obj") and is_hypothesis_test(item.obj)):
yield
elif "pytest_randomly" in sys.modules:
# See https://github.com/HypothesisWorks/hypothesis/issues/3041 - this
# branch exists to make it easier on external contributors, but should
# never run in our CI (because that would disable the check entirely).
assert not is_in_ci()
yield
else:
# We start by peturbing the state of the PRNG, because repeatedly
# leaking PRNG state resets state_after to the (previously leaked)
# state_before, and that just shows as "no use of random".
random.seed(independent_random.randrange(2**32))
before = random.getstate()
yield
after = random.getstate()
if before != after:
if after in random_states_after_tests:
raise Exception(
f"{item.nodeid!r} and {random_states_after_tests[after]!r} "
"both used the `random` module, and finished with the "
"same global `random.getstate()`; this is probably a nasty bug!"
)
random_states_after_tests[after] = item.nodeid