Skip to content

Commit e489053

Browse files
committed
🧪 Add local test running infra
1 parent f0d456a commit e489053

File tree

5 files changed

+243
-0
lines changed

5 files changed

+243
-0
lines changed

.coveragerc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[html]
2+
show_contexts = true
3+
skip_covered = false
4+
5+
[paths]
6+
_site-packages-to-src-mapping =
7+
src
8+
*/src
9+
*\src
10+
*/lib/pypy*/site-packages
11+
*/lib/python*/site-packages
12+
*\Lib\site-packages
13+
14+
[report]
15+
# `fail_under` is set here temporarily until it can be dropped:
16+
fail_under = 28
17+
skip_covered = true
18+
skip_empty = true
19+
show_missing = true
20+
exclude_also =
21+
^\s*@pytest\.mark\.xfail
22+
23+
[run]
24+
branch = true
25+
cover_pylib = false
26+
# https://coverage.rtfd.io/en/latest/contexts.html#dynamic-contexts
27+
# dynamic_context = test_function # conflicts with `pytest-cov` if set here
28+
parallel = true
29+
plugins =
30+
covdefaults
31+
relative_files = true
32+
source =
33+
.
34+
source_pkgs =
35+
normalize_needed_jobs_status

pyproject.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[dependency-groups]
2+
testing = [
3+
'covdefaults', # coveragepy settings as a plugin
4+
'coverage', # accessed directly from tox
5+
'coverage-enable-subprocess', # coveragepy plugin
6+
'pytest', # test runner
7+
'pytest-cov', # coveragepy integration
8+
'pytest-mock', # `mocker` fixture
9+
'pytest-subtests', # allows marking bits of test functions as subtests
10+
'pytest-xdist', # parallelism
11+
]

pytest.ini

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
[pytest]
2+
addopts =
3+
# `pytest-xdist`:
4+
--numprocesses=auto
5+
# NOTE: the plugin disabled because it's slower with so few tests
6+
--numprocesses=0
7+
8+
# Show 10 slowest invocations:
9+
--durations=10
10+
11+
# Report all the things == -rxXs:
12+
-ra
13+
14+
# Show values of the local vars in errors/tracebacks:
15+
--showlocals
16+
17+
# Autocollect and invoke the doctests from all modules:
18+
# https://docs.pytest.org/en/stable/doctest.html
19+
--doctest-modules
20+
21+
# Pre-load the `pytest-cov` plugin early:
22+
-p pytest_cov
23+
24+
# `pytest-cov`:
25+
--cov
26+
--cov-config=.coveragerc
27+
--cov-context=test
28+
--no-cov-on-fail
29+
30+
# Fail on config parsing warnings:
31+
# --strict-config
32+
33+
# Fail on non-existing markers:
34+
# * Deprecated since v6.2.0 but may be reintroduced later covering a
35+
# broader scope:
36+
# --strict
37+
# * Exists since v4.5.0 (advised to be used instead of `--strict`):
38+
--strict-markers
39+
40+
doctest_optionflags = ALLOW_UNICODE ELLIPSIS
41+
42+
# Marks tests with an empty parameterset as xfail(run=False)
43+
empty_parameter_set_mark = xfail
44+
45+
faulthandler_timeout = 30
46+
47+
filterwarnings =
48+
error
49+
50+
# https://docs.pytest.org/en/stable/usage.html#creating-junitxml-format-files
51+
junit_duration_report = call
52+
# xunit1 contains more metadata than xunit2 so it's better for CI UIs:
53+
junit_family = xunit1
54+
junit_logging = all
55+
junit_log_passing_tests = true
56+
junit_suite_name = alls_green_test_suite
57+
58+
# A mapping of markers to their descriptions allowed in strict mode:
59+
markers =
60+
61+
minversion = 6.1.0
62+
63+
# Optimize pytest's lookup by restricting potentially deep dir tree scan:
64+
norecursedirs =
65+
build
66+
dependencies
67+
dist
68+
docs
69+
.*
70+
*.egg
71+
*.egg-info
72+
*/*.egg-info
73+
*/**/*.egg-info
74+
*.dist-info
75+
*/*.dist-info
76+
*/**/*.dist-info
77+
78+
testpaths = tests/
79+
80+
xfail_strict = true

tests/smoke_test.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""Initial test infra smoke tests."""
2+
3+
import normalize_needed_jobs_status # pth via editable install in tox
4+
5+
6+
def test_smoke() -> None:
7+
"""Check that the imported module is truthy."""
8+
assert normalize_needed_jobs_status

tox.ini

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
[tox]
2+
isolated_build = true
3+
# Version 4.22 is the first one to implement support for dependency groups:
4+
minversion = 4.22
5+
6+
7+
[python-cli-options]
8+
byte-warnings = -b
9+
byte-errors = -bb
10+
max-isolation = -E -s -I
11+
some-isolation = -E -s
12+
warnings-to-errors = -Werror
13+
14+
15+
[testenv]
16+
description = Run pytest under {envpython}
17+
dependency_groups =
18+
testing
19+
commands =
20+
{envpython} \
21+
{[python-cli-options]byte-errors} \
22+
{[python-cli-options]max-isolation} \
23+
{[python-cli-options]warnings-to-errors} \
24+
-W 'ignore:Coverage failure::pytest_cov.plugin' \
25+
-m pytest \
26+
{tty:--color=yes} \
27+
{posargs:--cov-report=html:{envtmpdir}{/}htmlcov{/}}
28+
commands_post =
29+
-{envpython} \
30+
{[python-cli-options]byte-errors} \
31+
{[python-cli-options]max-isolation} \
32+
{[python-cli-options]warnings-to-errors} \
33+
-c \
34+
'import atexit, os, sys; \
35+
os.getenv("GITHUB_ACTIONS") == "true" or sys.exit(); \
36+
import coverage; \
37+
gh_summary_fd = open(\
38+
os.environ["GITHUB_STEP_SUMMARY"], encoding="utf-8", mode="a",\
39+
); \
40+
atexit.register(gh_summary_fd.close); \
41+
cov = coverage.Coverage(); \
42+
cov.load(); \
43+
cov.report(file=gh_summary_fd, output_format="markdown")'
44+
{envpython} \
45+
{[python-cli-options]byte-errors} \
46+
{[python-cli-options]max-isolation} \
47+
{[python-cli-options]warnings-to-errors} \
48+
-c \
49+
'import os, pathlib, sys; \
50+
os.getenv("GITHUB_ACTIONS") == "true" or sys.exit(); \
51+
cov_report_arg_prefix = "--cov-report=xml:"; \
52+
test_report_arg_prefix = "--junitxml="; \
53+
cov_reports = [\
54+
arg[len(cov_report_arg_prefix):] for arg in sys.argv \
55+
if arg.startswith(cov_report_arg_prefix)\
56+
]; \
57+
test_reports = [\
58+
arg[len(test_report_arg_prefix):] for arg in sys.argv \
59+
if arg.startswith(test_report_arg_prefix)\
60+
]; \
61+
cov_report_file = cov_reports[-1] if cov_reports else None; \
62+
test_report_file = test_reports[-1] if test_reports else None; \
63+
gh_output_fd = open(\
64+
os.environ["GITHUB_OUTPUT"], encoding="utf-8", mode="a",\
65+
); \
66+
cov_report_file and \
67+
print(f"cov-report-files={cov_report_file !s}", file=gh_output_fd); \
68+
test_report_file and \
69+
print(f"test-result-files={test_report_file !s}", file=gh_output_fd); \
70+
print("codecov-flags=pytest", file=gh_output_fd); \
71+
gh_output_fd.close()' \
72+
{posargs}
73+
# Print out the output coverage dir and a way to serve html:
74+
{envpython} \
75+
{[python-cli-options]byte-errors} \
76+
{[python-cli-options]max-isolation} \
77+
{[python-cli-options]warnings-to-errors} \
78+
-c\
79+
'import pathlib, shlex, sys; \
80+
cov_html_report_arg_prefix = "--cov-report=html:"; \
81+
cov_html_reports = [\
82+
arg[len(cov_html_report_arg_prefix):] for arg in sys.argv \
83+
if arg.startswith(cov_html_report_arg_prefix)\
84+
]; \
85+
cov_html_reports or sys.exit(); \
86+
cov_html_report_dir = pathlib.Path(cov_html_reports[-1]); \
87+
index_file = cov_html_report_dir / "index.html";\
88+
index_file.exists() or sys.exit(); \
89+
html_url = f"file://\{index_file\}";\
90+
browse_cmd = shlex.join(("python3", "-Im", "webbrowser", html_url)); \
91+
serve_cmd = shlex.join((\
92+
"python3", "-Im", "http.server", \
93+
"--directory", str(cov_html_report_dir), "0", \
94+
)); \
95+
print(f"\nTo open the HTML coverage report, run\n\n\
96+
\t\{browse_cmd !s\}\n");\
97+
print(f"To serve \
98+
the HTML coverage report with a local web server, use\n\n\
99+
\t\{serve_cmd !s\}\n")' \
100+
{posargs:--cov-report=html:{envtmpdir}{/}htmlcov{/}}
101+
package = editable
102+
pass_env =
103+
CI
104+
GITHUB_*
105+
SSH_AUTH_SOCK
106+
TERM
107+
set_env =
108+
COVERAGE_PROCESS_START = {toxinidir}{/}.coveragerc
109+
wheel_build_env = .pkg

0 commit comments

Comments
 (0)