Skip to content

Commit d66940b

Browse files
committed
ENH: Added spin coverage
1 parent 1713846 commit d66940b

2 files changed

Lines changed: 121 additions & 0 deletions

File tree

example_pkg/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ package = 'example_pkg'
2727

2828
"Build" = [
2929
"spin.cmds.meson.build",
30+
"spin.cmds.meson.coverage",
3031
"spin.cmds.meson.test",
3132
"spin.cmds.build.sdist",
3233
"spin.cmds.build.wheel",

spin/cmds/meson.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,126 @@ def test(
680680
raise SystemExit(pytest_p.returncode)
681681

682682

683+
def _resolve_cov_report(report: str, base: Path) -> str:
684+
"""Resolve a --cov-report value, rebasing relative paths under `base`."""
685+
if ":" not in report:
686+
return report
687+
688+
fmt, dest = report.split(":", 1)
689+
dest_path = Path(dest)
690+
if not dest_path.is_absolute():
691+
dest_path = base / dest_path
692+
if dest_path.exists():
693+
click.secho(f"Removing `{dest_path}`", fg="bright_yellow")
694+
if dest_path.is_dir():
695+
shutil.rmtree(dest_path)
696+
else:
697+
dest_path.unlink()
698+
dest_path.parent.mkdir(parents=True, exist_ok=True)
699+
return f"{fmt}:{dest_path}"
700+
701+
702+
@click.command()
703+
@click.argument("pytest_args", nargs=-1)
704+
@click.option(
705+
"-j",
706+
"n_jobs",
707+
metavar="N_JOBS",
708+
default="1",
709+
help="Number of parallel jobs for testing with pytest-xdist.",
710+
)
711+
@click.option(
712+
"--tests",
713+
"-t",
714+
metavar="TESTS",
715+
help="Which tests to run. Can be a module, function, class, or method.",
716+
)
717+
@click.option("--verbose", "-v", is_flag=True, default=False)
718+
@click.option(
719+
"--cov-report",
720+
"cov_report",
721+
multiple=True,
722+
metavar="TYPE",
723+
help=(
724+
"Coverage report type passed to pytest-cov (e.g. term, term-missing, "
725+
"html:dir, xml:file.xml, json:file.json, lcov:file.lcov, annotate:dir). "
726+
"Can be specified multiple times. Defaults to `term`."
727+
),
728+
)
729+
@build_option
730+
@build_dir_option
731+
@click.pass_context
732+
def coverage(
733+
ctx,
734+
*,
735+
pytest_args,
736+
n_jobs,
737+
tests,
738+
verbose,
739+
cov_report,
740+
build=None,
741+
build_dir=None,
742+
):
743+
"""📊 Run tests with Python code coverage
744+
745+
Generate coverage reports using pytest-cov. By default, a terminal
746+
report is printed. Supports any report type that pytest-cov supports.
747+
748+
For file-based reports, use the `type:path` format. Relative paths
749+
are placed under `build/coverage/`.
750+
751+
To generate an HTML report:
752+
753+
spin coverage --cov-report html:htmlcov
754+
755+
Multiple report types can be specified:
756+
757+
spin coverage --cov-report term-missing --cov-report xml:coverage.xml
758+
759+
Run coverage on specific tests:
760+
761+
\b
762+
spin coverage -t example_pkg.echo
763+
spin coverage example_pkg/tests
764+
765+
Pass additional pytest arguments after `--`:
766+
767+
spin coverage -- --durations=10 -k "test_foo"
768+
769+
Run tests in parallel (requires pytest-xdist):
770+
771+
spin coverage -j auto
772+
"""
773+
cfg = get_config()
774+
package = cfg.get("tool.spin.package", None)
775+
if package is None:
776+
click.secho(
777+
"Please specify `package = packagename` under `tool.spin` section of `pyproject.toml`",
778+
fg="bright_red",
779+
)
780+
raise SystemExit(1)
781+
782+
# Build --cov-report flags, resolving relative paths under build/coverage/
783+
coverage_base = Path.cwd() / "build" / "coverage"
784+
cov_args = [f"--cov={package}"]
785+
cov_reports = cov_report or ("term",)
786+
for report in cov_reports:
787+
cov_args.append(f"--cov-report={_resolve_cov_report(report, coverage_base)}")
788+
789+
# Prepend cov args so user's `--` args come after
790+
pytest_args = tuple(cov_args) + (pytest_args or ())
791+
792+
ctx.invoke(
793+
test,
794+
pytest_args=pytest_args,
795+
n_jobs=n_jobs,
796+
tests=tests,
797+
verbose=verbose,
798+
build=build,
799+
build_dir=build_dir,
800+
)
801+
802+
683803
@click.command()
684804
@click.option(
685805
"--code", "-c", metavar="CODE", help="Python program passed in as a string"

0 commit comments

Comments
 (0)