forked from dagster-io/dagster
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconftest.py
More file actions
160 lines (119 loc) · 5.22 KB
/
conftest.py
File metadata and controls
160 lines (119 loc) · 5.22 KB
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import os
import time
import warnings
from dataclasses import dataclass
from functools import lru_cache
import pytest
try:
from dagster import BetaWarning, PreviewWarning, SupersessionWarning
warnings.filterwarnings("ignore", category=BetaWarning)
warnings.filterwarnings("ignore", category=PreviewWarning)
warnings.filterwarnings("ignore", category=SupersessionWarning)
except ImportError:
pass # Not all test suites have dagster installed
@dataclass(frozen=True)
class TestId:
scope: str
name: str
@lru_cache
def buildkite_quarantined_tests(annotation) -> set[TestId]:
quarantined_tests = set()
if os.getenv("BUILDKITE") or os.getenv("LOCAL_BUILDKITE_QUARANTINE"):
# Run our full test suite - warts and all - on the release branch
if os.getenv("BUILDKITE_BRANCH", "").startswith("release-"):
return quarantined_tests
try:
import requests
token = os.getenv("BUILDKITE_TEST_QUARANTINE_TOKEN")
org_slug = os.getenv("BUILDKITE_ORGANIZATION_SLUG")
suite_slug = os.getenv("BUILDKITE_TEST_SUITE_SLUG")
headers = {"Authorization": f"Bearer {token}"}
url = f"https://api.buildkite.com/v2/analytics/organizations/{org_slug}/suites/{suite_slug}/tests/{annotation}"
start_time = time.time()
timeout = 10
while url and time.time() - start_time < timeout:
response = requests.get(url, headers=headers)
response.raise_for_status()
for test in response.json():
scope = test.get("scope", "")
name = test.get("name", "")
quarantined_test = TestId(scope, name)
quarantined_tests.add(quarantined_test)
link_header = response.headers.get("Link", "")
next_url = None
for part in link_header.split(","):
if 'rel="next"' in part:
next_url = part[part.find("<") + 1 : part.find(">")]
break
url = next_url
except Exception as e:
print(e) # noqa
return quarantined_tests
def pytest_addoption(parser):
parser.addoption(
"--split", action="store", default=None, help="Split test selection (e.g., 0/3)"
)
def pytest_configure(config):
# Create a section break in the logs any time Buildkite invokes pytest
# https://buildkite.com/docs/pipelines/managing-log-output
# https://docs.pytest.org/en/7.1.x/reference/reference.html?highlight=pytest_configure#pytest.hookspec.pytest_configure
if os.getenv("BUILDKITE"):
print("+++ Running :pytest: PyTest") # noqa
# https://docs.pytest.org/en/7.1.x/example/markers.html#custom-marker-and-command-line-option-to-control-test-runs
config.addinivalue_line(
"markers", "integration: mark test to skip if DISABLE_INTEGRATION_TESTS is set."
)
def pytest_runtest_setup(item):
# https://buildkite.com/docs/apis/rest-api/test-engine/quarantine#list-quarantined-tests
# Buildkite Test Engine marks unreliable tests as muted and triages them out to owning teams to improve.
# We pull this list of tests at the beginning of each pytest session and add soft xfail markers to each
# quarantined test.
try:
muted = buildkite_quarantined_tests("muted")
skipped = buildkite_quarantined_tests("skipped")
if muted or skipped:
# https://github.com/buildkite/test-collector-python/blob/6fba081a2844d6bdec8607942eee48a03d60cd40/src/buildkite_test_collector/pytest_plugin/buildkite_plugin.py#L22-L27
chunks = item.nodeid.split("::")
scope = "::".join(chunks[:-1])
name = chunks[-1]
test = TestId(scope, name)
if test in muted:
item.add_marker(pytest.mark.xfail(reason="Test muted in Buildkite.", strict=False))
if test in skipped:
item.add_marker(pytest.skip(reason="Test skipped in Buildkite."))
except Exception as e:
print(e) # noqa
try:
next(item.iter_markers("integration"))
if os.getenv("CI_DISABLE_INTEGRATION_TESTS"):
pytest.skip("Integration tests are disabled")
except StopIteration:
pass
@pytest.hookimpl(trylast=True)
def pytest_collection_modifyitems(config, items):
"""Split pytest collection.
Example usage:
pytest --split 1/2 # run half the tests
pytest --split 2/2 # run the other half the tests
"""
split_option = config.getoption("--split")
if not split_option:
return
try:
k, n = map(int, split_option.split("/"))
except ValueError:
raise pytest.UsageError(
"--split must be in the form numerator/denominator (e.g. --split=1/3)"
)
if k <= 0:
raise pytest.UsageError("--split numerator must be > 0")
if k > n:
raise pytest.UsageError("--split numerator must be smaller than denominator")
total = len(items)
start = total * (k - 1) // n
end = total * k // n
selected = items[start:end]
deselected = items[:start] + items[end:]
if deselected:
config.hook.pytest_deselected(items=deselected)
items[:] = selected