Просмотр исходного кода

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

rdb 5 дней назад
Родитель
Сommit
48215debb4
1 измененных файлов с 123 добавлено и 0 удалено
  1. 123 0
      tests/conftest.py

+ 123 - 0
tests/conftest.py

@@ -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,
+    }
+
+
[email protected](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;")
+    )