forked from pymc-devs/pymc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcheck_all_tests_are_covered.py
119 lines (98 loc) · 4.28 KB
/
check_all_tests_are_covered.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
"""
In .github/workflows/pytest.yml, tests are split between multiple jobs.
Here, we check that the jobs ignored by the first job actually end up getting
run by the other jobs.
This is intended to be used as a pre-commit hook, see `.pre-commit-config.yaml`.
You can run it manually with `pre-commit run check-no-tests-are-ignored --all`.
"""
import itertools
import logging
import os
from pathlib import Path
import pandas
import yaml
_log = logging.getLogger(__file__)
logging.basicConfig(level=logging.DEBUG)
def find_testfiles():
dp_repo = Path(__file__).parent.parent
all_tests = {
str(fp.relative_to(dp_repo)).replace(os.sep, "/")
for fp in (dp_repo / "tests").glob("**/test_*.py")
}
_log.info("Found %i tests in total.", len(all_tests))
return all_tests
def from_yaml():
"""Determines how often each test file is run per platform and floatX setting.
An exception is raised if tests run multiple times with the same configuration.
"""
# First collect the matrix definitions from testing workflows
matrices = {}
for wf in ["tests.yml"]:
wfname = wf.rstrip(".yml")
wfdef = yaml.safe_load(open(Path(".github", "workflows", wf)))
for jobname, jobdef in wfdef["jobs"].items():
matrix = jobdef.get("strategy", {}).get("matrix", {})
if matrix:
matrices[(wfname, jobname)] = matrix
else:
_log.warning("No matrix in %s/%s", wf, jobname)
# Now create an empty DataFrame to count based on OS/floatX/testfile
all_os = []
all_floatX = []
for matrix in matrices.values():
all_os += matrix["os"]
all_floatX += matrix["floatx"]
all_os = tuple(sorted(set(all_os)))
all_floatX = tuple(sorted(set(all_floatX)))
all_tests = find_testfiles()
df = pandas.DataFrame(
columns=pandas.MultiIndex.from_product(
[sorted(all_floatX), sorted(all_os)], names=["floatX", "os"]
),
index=pandas.Index(sorted(all_tests), name="testfile"),
)
df.loc[:, :] = 0
# Count how often the testfiles are included in job definitions
for matrix in matrices.values():
for os_, floatX, subset in itertools.product(
matrix["os"], matrix["floatx"], matrix["test-subset"]
):
lines = [k for k in subset.split("\n") if k]
if "windows" in os_:
# Windows jobs need \ in line breaks within the test-subset!
# The following checks that these trailing \ are present in
# all items except the last.
if lines and lines[-1].endswith(" \\"):
raise Exception(
f"Last entry '{lines}' in Windows test subset should end WITHOUT ' \\'."
)
for line in lines[:-1]:
if not line.endswith(" \\"):
raise Exception(f"Missing ' \\' after '{line}' in Windows test-subset.")
lines = [line.rstrip(" \\") for line in lines]
# Unpack lines with >1 item
testfiles = []
for line in lines:
testfiles += line.split(" ")
ignored = {item[8:].lstrip(" =") for item in testfiles if item.startswith("--ignore")}
included = {item for item in testfiles if item and not item.startswith("--ignore")}
if ignored and not included:
# if no testfile is specified explicitly pytest runs all except the ignored ones
included = all_tests - ignored
for testfile in included:
df.loc[testfile, (floatX, os_)] += 1
ignored_by_all = set(df[df.eq(0).all(axis=1)].index)
run_multiple_times = set(df[df.gt(1).any(axis=1)].index)
# Print summary, warnings and raise errors on unwanted configurations
_log.info("Number of test runs (❌=0, ✅=once)\n%s", df.replace(0, "❌").replace(1, "✅"))
if ignored_by_all:
raise AssertionError(
f"{len(ignored_by_all)} tests are completely ignored:\n{ignored_by_all}"
)
if run_multiple_times:
raise AssertionError(
f"{len(run_multiple_times)} tests are run multiple times with the same OS and floatX setting:\n{run_multiple_times}"
)
return
if __name__ == "__main__":
from_yaml()