tests: When running in GitHub Actions, write GFM tests summary

This commit is contained in:
rdb 2026-01-24 14:03:29 +01:00
parent 4b98910789
commit 48215debb4
1 changed files with 123 additions and 0 deletions

View File

@ -1,3 +1,4 @@
import os
import sys
import pytest
from panda3d import core
@ -99,3 +100,125 @@ def gsg(graphics_pipe, graphics_engine):
if buffer is not None:
graphics_engine.remove_window(buffer)
def pytest_configure(config):
"""Initialize the failure collector."""
config._github_summary_failures = []
config._github_summary_counts = {
"passed": 0,
"failed": 0,
"skipped": 0,
"xfailed": 0,
}
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""Collect information about test outcomes."""
outcome = yield
report = outcome.get_result()
if report.when == "call":
counts = item.config._github_summary_counts
if report.passed:
if hasattr(report, "wasxfail"):
# xpass - passed but was expected to fail (treat as passed for now)
counts["passed"] += 1
else:
counts["passed"] += 1
elif report.failed:
counts["failed"] += 1
failure_info = {
"nodeid": report.nodeid,
"location": report.location,
"longrepr": str(report.longrepr) if report.longrepr else None,
"sections": report.sections,
"duration": report.duration,
}
item.config._github_summary_failures.append(failure_info)
elif report.skipped:
if hasattr(report, "wasxfail"):
counts["xfailed"] += 1
else:
counts["skipped"] += 1
elif report.when == "setup" and report.skipped:
# Handle skip during setup (e.g., skipif, skip markers)
item.config._github_summary_counts["skipped"] += 1
def pytest_sessionfinish(session, exitstatus):
"""Write GitHub step summary if GITHUB_STEP_SUMMARY is set."""
summary_path = os.environ.get("GITHUB_STEP_SUMMARY")
if not summary_path:
return
failures = session.config._github_summary_failures
counts = session.config._github_summary_counts
lines = []
# Build status parts for the summary line
status_parts = []
if counts["failed"]:
status_parts.append(f"**{counts['failed']}** failed")
if counts["passed"]:
status_parts.append(f"**{counts['passed']}** passed")
if counts["skipped"]:
status_parts.append(f"**{counts['skipped']}** skipped")
if counts["xfailed"]:
status_parts.append(f"**{counts['xfailed']}** xfailed")
total = sum(counts.values())
status_line = ", ".join(status_parts) + f" (**{total}** total)\n"
# Header with overall status
if counts["failed"] == 0:
lines.append(f"### :white_check_mark: All tests passed (Python {sys.version_info.major}.{sys.version_info.minor})\n\n")
else:
lines.append(f"### :x: Test failures (Python {sys.version_info.major}.{sys.version_info.minor})\n\n")
lines.append(status_line)
lines.append("\n")
# Each failure in a collapsible section
for failure in failures:
nodeid = failure["nodeid"]
duration = failure["duration"]
lines.append(f"<details>\n<summary><code>{_escape_html(nodeid)}</code> ({duration:.2f}s)</summary>\n")
# Traceback / longrepr
if failure["longrepr"]:
lines.append("\n#### Traceback\n")
lines.append("```python\n")
lines.append(failure["longrepr"])
if not failure["longrepr"].endswith("\n"):
lines.append("\n")
lines.append("```\n")
# Captured output sections (stdout, stderr, log, etc.)
for section_name, section_content in failure["sections"]:
if section_content.strip():
lines.append(f"\n#### {_escape_html(section_name)}\n")
lines.append("```\n")
lines.append(section_content)
if not section_content.endswith("\n"):
lines.append("\n")
lines.append("```\n")
lines.append("\n</details>\n\n")
with open(summary_path, "a", encoding="utf-8") as f:
f.writelines(lines)
def _escape_html(text: str) -> str:
"""Escape HTML special characters for safe rendering in markdown."""
return (
text.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
)