feat(i18n): tools + landing page + doorstop generator in English
Phase 1 of full English translation: - generate_doorstop_items.py: all 55 items (SG/SYS/SWE/SA/SWA) rewritten in English - generate_landing_page.py: full UI labels, KPI cards, section headings in English - traceability.py: docstring, error messages, HTML headers in English - generate_test_report.py: report content + table headers in English - All 55 markdown items in safety/sg/, reqs/, arch/ regenerated in English Still to come: - demo-epb filled Word docs (PID, plans, safety, manuals, audit artefacts) - Code comments + test names + CI workflow step names - README + dev-process repo templates
This commit is contained in:
+349
-333
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Erzeugt eine HTML-Startseite (Dashboard) fuer demo-epb.
|
||||
Generate an HTML landing page (dashboard) for demo-epb.
|
||||
|
||||
Scant das Repo nach Word-Dokumenten, Reports, Code, Tests, Architektur, und
|
||||
schreibt build/index.html mit klickbaren Links.
|
||||
Scans the repo for Word documents, reports, code, tests, architecture, and
|
||||
writes build/index.html with clickable links.
|
||||
|
||||
Run nach `make test && make coverage && make docs && make test-report && python3 tools/traceability.py publish docs/traceability && python3 tools/render_plantuml.py`.
|
||||
Run after `make test && make coverage && make docs && make test-report &&
|
||||
python3 tools/traceability.py publish docs/traceability &&
|
||||
python3 tools/render_plantuml.py`.
|
||||
|
||||
Output:
|
||||
build/index.html — standalone, oeffnen mit Browser
|
||||
build/index.html — standalone, open in a browser
|
||||
|
||||
Verwendung im Release-Bundle:
|
||||
- Liegt bei demo-epb-vX.Y.Z/index.html
|
||||
- Verlinkt alle anderen Bundle-Inhalte relativ
|
||||
Use in the release bundle:
|
||||
- Lives at demo-epb-vX.Y.Z/index.html
|
||||
- Links to all other bundle contents using relative paths
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -66,20 +68,11 @@ def collect_docs(rel_dir: str, in_release: bool = False) -> list[tuple[str, str]
|
||||
if not d.exists():
|
||||
return out
|
||||
for f in sorted(d.glob("*.docx")):
|
||||
# In release bundle, paths are different; here we use relative-to-repo.
|
||||
href = os.path.relpath(f, REPO)
|
||||
# If running for in_release context, paths need adjustment, but for now
|
||||
# we always use repo-relative.
|
||||
out.append((f.stem, href))
|
||||
return out
|
||||
|
||||
|
||||
def status_for(path: Path) -> str:
|
||||
if path.exists():
|
||||
return "ok"
|
||||
return "missing"
|
||||
|
||||
|
||||
def kpi_card(label: str, value: str, sub: str = "", color: str = "#1f3864") -> str:
|
||||
return f"""
|
||||
<div class='kpi'>
|
||||
@@ -92,7 +85,7 @@ def kpi_card(label: str, value: str, sub: str = "", color: str = "#1f3864") -> s
|
||||
|
||||
def doc_section(title: str, docs: list[tuple[str, str]], description: str = "") -> str:
|
||||
if not docs:
|
||||
items = "<li class='cnt'>— keine Dokumente —</li>"
|
||||
items = "<li class='cnt'>— no documents —</li>"
|
||||
else:
|
||||
items = "\n".join(
|
||||
f'<li><a href="{html.escape(href)}">{html.escape(name)}</a></li>'
|
||||
@@ -109,7 +102,7 @@ def doc_section(title: str, docs: list[tuple[str, str]], description: str = "")
|
||||
|
||||
def report_link(name: str, href: str, exists: bool, desc: str) -> str:
|
||||
cls = "ok" if exists else "missing"
|
||||
label = name + ("" if exists else " (nicht generiert — Coverage/Build laufen lassen)")
|
||||
label = name + ("" if exists else " (not generated — run coverage/build)")
|
||||
if exists:
|
||||
return (f"<li><a href='{html.escape(href)}'>{html.escape(label)}</a> "
|
||||
f"<span class='cnt'>— {html.escape(desc)}</span></li>")
|
||||
@@ -139,9 +132,9 @@ def main() -> int:
|
||||
ncs = collect_docs("docs/non-conformities")
|
||||
misra_r = collect_docs("misra/records")
|
||||
|
||||
# Reports — Links zeigen auf BUNDLE-Pfade (relative zum index.html im Deploy).
|
||||
# Die CI-Pipeline deployt die Artefakte in genau diese Pfade,
|
||||
# darum ist deren Existenz hier irrelevant — Links werden immer emittiert.
|
||||
# Reports — links target BUNDLE paths (relative to index.html in deploy).
|
||||
# The CI pipeline copies artifacts to exactly these paths, so the links
|
||||
# always resolve in the deployed bundle.
|
||||
rep_paths = {
|
||||
"coverage": "coverage/index.html",
|
||||
"test": "reports/test-report.html",
|
||||
@@ -149,17 +142,11 @@ def main() -> int:
|
||||
"trace": "traceability/index.html",
|
||||
"cppcheck": "reports/cppcheck-report.xml",
|
||||
}
|
||||
# Existence-Check zum Anzeigen "Generated?" — gegen den CI/lokalen Build-Pfad.
|
||||
rep_cov_built = (REPO / "build" / "coverage-html" / "index.html").exists()
|
||||
rep_test_built = (REPO / "build" / "test-report.html").exists()
|
||||
rep_api_built = (REPO / "build" / "api-doc" / "html" / "index.html").exists()
|
||||
rep_trace_built = (REPO / "docs" / "traceability" / "index.html").exists()
|
||||
rep_cpp_built = (REPO / "build" / "cppcheck-report.xml").exists()
|
||||
|
||||
html_body = f"""<!doctype html>
|
||||
<html lang='de'><head>
|
||||
<html lang='en'><head>
|
||||
<meta charset='utf-8'>
|
||||
<title>demo-epb {html.escape(tag)} — Projekt-Dashboard</title>
|
||||
<title>demo-epb {html.escape(tag)} — Project Dashboard</title>
|
||||
<style>
|
||||
:root {{ color-scheme: light; }}
|
||||
body {{ font-family: -apple-system, "Segoe UI", sans-serif; margin: 0; padding: 0; color: #222; background: #f5f6f8; }}
|
||||
@@ -187,71 +174,64 @@ footer {{ text-align: center; color: #888; padding: 30px; font-size: 13px; }}
|
||||
</style></head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>demo-epb — Elektrische Parkbremse</h1>
|
||||
<div class='meta'>Version <strong>{html.escape(tag)}</strong> · Commit <code>{html.escape(sha)}</code> · Generiert {html.escape(now)}</div>
|
||||
<h1>demo-epb — Electric Parking Brake</h1>
|
||||
<div class='meta'>Version <strong>{html.escape(tag)}</strong> · Commit <code>{html.escape(sha)}</code> · Generated {html.escape(now)}</div>
|
||||
</header>
|
||||
<main>
|
||||
|
||||
<div class='banner'>
|
||||
<strong>Demo-Projekt:</strong> Vollstaendige Demo des
|
||||
<strong>Demo project:</strong> Complete demonstration of the
|
||||
<a href='https://gitea.slohmaier.com/slohmaier/dev-process'>slohmaier Dev Process</a>.
|
||||
Diese Software ist bewusst kein Produktivcode, sondern Showcase der Engineering-Methodik.
|
||||
This software is intentionally not production code; it is a showcase of the engineering method.
|
||||
</div>
|
||||
|
||||
<div class='kpis'>
|
||||
{kpi_card("Safety Goals", str(n_sg), "ASIL D/D/A/C/B", "#d62728")}
|
||||
{kpi_card("System Reqs", str(n_sys), f"in reqs/sys/")}
|
||||
{kpi_card("SW Reqs", str(n_swe), f"in reqs/swe/")}
|
||||
{kpi_card("Arch-Elemente", f"{n_sa+n_swa}", f"{n_sa} SA + {n_swa} SWA")}
|
||||
{kpi_card("Komponenten", f"{n_impl}", f"+ {n_stubs} Stubs", "#2ca02c")}
|
||||
{kpi_card("Unit-Tests", str(n_tests), "Alle gruen", "#2ca02c")}
|
||||
{kpi_card("Arch Elements", f"{n_sa+n_swa}", f"{n_sa} SA + {n_swa} SWA")}
|
||||
{kpi_card("Components", f"{n_impl}", f"+ {n_stubs} stubs", "#2ca02c")}
|
||||
{kpi_card("Unit tests", str(n_tests), "All green", "#2ca02c")}
|
||||
</div>
|
||||
|
||||
<div class='cols'>
|
||||
|
||||
<section>
|
||||
<h2>Plaene (Word)</h2>
|
||||
<ul>
|
||||
"""
|
||||
for name, href in plans:
|
||||
if not href.startswith("docs/safety") and not href.startswith("docs/manuals"):
|
||||
html_body += f" <li><a href='{html.escape(href)}'>{html.escape(name)}</a></li>\n"
|
||||
html_body += " </ul></section>\n"
|
||||
|
||||
html_body += doc_section("Funktionale Sicherheit (Word)", safety,
|
||||
"HARA, Safety Case, FMEDA, Compliance, Verification, Tool-Qualification")
|
||||
|
||||
html_body += "<div class='cols'>"
|
||||
html_body += doc_section("Plans (Word)", plans,
|
||||
"Project Manual, PID, PM Plan, QA Plan, SWE Plan, Test Plan, CM Plan, RM Plan")
|
||||
html_body += doc_section("Functional Safety (Word)", safety,
|
||||
"HARA, Safety Case, FMEDA, Compliance, Verification, Tool Qualification")
|
||||
html_body += "</div><div class='cols'>"
|
||||
|
||||
html_body += doc_section("Manuals (Word)", manuals,
|
||||
"End-User + Werkstatt-Doku")
|
||||
"End-user + workshop documentation")
|
||||
|
||||
audit_docs = reviews + ncs + misra_r
|
||||
html_body += doc_section("Audit-Artefakte (Word)", audit_docs,
|
||||
"Reviews, Non-Conformities, MISRA-Deviation-Records")
|
||||
html_body += doc_section("Audit Artifacts (Word)", audit_docs,
|
||||
"Reviews, non-conformities, MISRA deviation records")
|
||||
|
||||
html_body += "</div>"
|
||||
|
||||
# Reports — Links immer setzen, Bundle-Pfade.
|
||||
html_body += "<section><h2>Engineering-Reports (CI-generiert)</h2><ul>\n"
|
||||
html_body += report_link("Traceability-Matrix", rep_paths["trace"], True,
|
||||
"SG -> SYS -> SA, SWE -> SWA -> Code+Test, bidirektional verifiziert")
|
||||
html_body += report_link("Test-Summary", rep_paths["test"], True,
|
||||
f"{n_tests} Unit-Tests mit Anforderungs-Mapping")
|
||||
# Reports — always emit links, bundle-relative paths.
|
||||
html_body += "<section><h2>Engineering Reports (CI-generated)</h2><ul>\n"
|
||||
html_body += report_link("Traceability Matrix", rep_paths["trace"], True,
|
||||
"SG -> SYS -> SA, SWE -> SWA -> Code+Test, verified bidirectionally")
|
||||
html_body += report_link("Test Summary", rep_paths["test"], True,
|
||||
f"{n_tests} unit tests with requirement mapping")
|
||||
html_body += report_link("Coverage (gcov/lcov)", rep_paths["coverage"], True,
|
||||
"Statement + Branch Coverage, klickbar bis Zeilen-Level")
|
||||
html_body += report_link("API-Dokumentation (Doxygen)", rep_paths["api"], True,
|
||||
"Alle Header + Funktionen, mit @arch/@reqs/@asil")
|
||||
html_body += report_link("Cppcheck-Report (HTML)", "reports/cppcheck/index.html", True,
|
||||
"Statische Analyse + MISRA-Findings, klickbar pro Datei")
|
||||
html_body += report_link("Cppcheck-Report (XML, Roh)", rep_paths["cppcheck"], True,
|
||||
"Maschinen-lesbares Format fuer eigene Tools")
|
||||
"Statement + branch coverage, drill down to line level")
|
||||
html_body += report_link("API Documentation (Doxygen)", rep_paths["api"], True,
|
||||
"All headers + functions, with @arch/@reqs/@asil")
|
||||
html_body += report_link("Cppcheck Report (HTML)", "reports/cppcheck/index.html", True,
|
||||
"Static analysis + MISRA findings, clickable per file")
|
||||
html_body += report_link("Cppcheck Report (XML, raw)", rep_paths["cppcheck"], True,
|
||||
"Machine-readable format for your own tools")
|
||||
html_body += "</ul></section>"
|
||||
|
||||
# Diagrams
|
||||
diagrams = sorted((REPO / "docs" / "diagrams").glob("*.svg"))
|
||||
if diagrams:
|
||||
html_body += "<section><h2>Architektur-Diagramme (PlantUML)</h2><ul>"
|
||||
html_body += "<section><h2>Architecture Diagrams (PlantUML)</h2><ul>"
|
||||
for d in diagrams:
|
||||
href = os.path.relpath(d, REPO)
|
||||
html_body += f" <li><a href='{html.escape(href)}'>{html.escape(d.stem)}</a></li>\n"
|
||||
@@ -260,24 +240,24 @@ footer {{ text-align: center; color: #888; padding: 30px; font-size: 13px; }}
|
||||
# Source code links
|
||||
html_body += """
|
||||
<section>
|
||||
<h2>Source-Code</h2>
|
||||
<h2>Source Code</h2>
|
||||
<ul>
|
||||
<li><a href='src/safety_manager.c'>safety_manager.c</a> — Safety Manager (ASIL-D, Hill-Hold + Auto-Apply + Drive-Away)</li>
|
||||
<li><a href='src/apply_controller.c'>apply_controller.c</a> — Apply Controller (ASIL-D, State Machine)</li>
|
||||
<li><a href='src/actuator_driver.c'>actuator_driver.c</a> — Actuator Driver (ASIL-B, Overcurrent-Cutoff)</li>
|
||||
<li><a href='src/safety_manager.c'>safety_manager.c</a> — Safety Manager (ASIL-D: Hill-Hold + Auto-Apply + Drive-Away)</li>
|
||||
<li><a href='src/apply_controller.c'>apply_controller.c</a> — Apply Controller (ASIL-D, state machine)</li>
|
||||
<li><a href='src/actuator_driver.c'>actuator_driver.c</a> — Actuator Driver (ASIL-B, overcurrent cutoff)</li>
|
||||
<li><a href='src/switch_debouncer.c'>switch_debouncer.c</a> — Switch Debouncer (QM)</li>
|
||||
<li class='cnt'>Plus 6 Stub-Header in <a href='src/stubs/'>src/stubs/</a></li>
|
||||
<li class='cnt'>Plus 6 stub headers in <a href='src/stubs/'>src/stubs/</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
"""
|
||||
|
||||
html_body += f"""
|
||||
<section>
|
||||
<h2>Externe Links</h2>
|
||||
<h2>External Links</h2>
|
||||
<ul>
|
||||
<li><a href='https://gitea.slohmaier.com/slohmaier/demo-epb'>Gitea-Repo</a></li>
|
||||
<li><a href='https://gitea.slohmaier.com/slohmaier/demo-epb'>Gitea repo</a></li>
|
||||
<li><a href='https://gitea.slohmaier.com/slohmaier/demo-epb/releases'>Releases</a></li>
|
||||
<li><a href='https://gitea.slohmaier.com/slohmaier/dev-process'>Methodik-Repo (dev-process)</a></li>
|
||||
<li><a href='https://gitea.slohmaier.com/slohmaier/dev-process'>Methodology repo (dev-process)</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""
|
||||
Erzeugt einen Test-Summary-Report aus dem Output unserer Unit-Tests.
|
||||
|
||||
Liest die Test-Output-Datei (build/test-output.txt) und erzeugt:
|
||||
Reads the test-output file (build/test-output.txt) and produces:
|
||||
- build/test-report.md
|
||||
- build/test-report.html
|
||||
|
||||
@@ -67,7 +67,7 @@ def main() -> int:
|
||||
output = TEST_OUTPUT.read_text()
|
||||
results = parse_output(output)
|
||||
if not results:
|
||||
print("ERROR: keine Test-Suite im Output gefunden.")
|
||||
print("ERROR: no test suite found in the output.")
|
||||
return 1
|
||||
|
||||
total = sum(r["total"] for r in results)
|
||||
@@ -77,11 +77,11 @@ def main() -> int:
|
||||
|
||||
# Markdown
|
||||
md = [f"# demo-epb — Test Summary Report\n\n",
|
||||
f"**Datum:** {now}\n\n",
|
||||
f"**Gesamt:** {total} Tests, {passed} bestanden, {failed} fehlgeschlagen\n\n",
|
||||
f"**Date:** {now}\n\n",
|
||||
f"**Total:** {total} tests, {passed} passed, {failed} failed\n\n",
|
||||
f"**Status:** {'PASS' if failed == 0 else 'FAIL'}\n\n",
|
||||
"## Pro Test-Suite\n\n",
|
||||
"| Suite | Anzahl | Bestanden | Fehlgeschlagen | Anforderungen |\n",
|
||||
"## Per Test Suite\n\n",
|
||||
"| Suite | Count | Passed | Failed | Requirements |\n",
|
||||
"|-------|--------|-----------|-----------------|---------------|\n"]
|
||||
for r in results:
|
||||
reqs = ", ".join(reqs_for(r["binary"])) or "—"
|
||||
@@ -113,12 +113,12 @@ def main() -> int:
|
||||
".pass-badge{background:#0a0}.fail-badge{background:#c00}",
|
||||
"</style></head><body>",
|
||||
"<h1>demo-epb — Test Summary Report</h1>",
|
||||
f"<p><strong>Datum:</strong> {now}</p>",
|
||||
f"<p><strong>Gesamt:</strong> {total} Tests, {passed} bestanden, {failed} fehlgeschlagen — "
|
||||
f"<p><strong>Date:</strong> {now}</p>",
|
||||
f"<p><strong>Total:</strong> {total} tests, {passed} passed, {failed} failed — "
|
||||
f"<span class='badge {badge_cls}'>{badge_txt}</span></p>",
|
||||
"<h2>Pro Test-Suite</h2>",
|
||||
"<table><tr><th>Suite</th><th>Anzahl</th><th>Bestanden</th>"
|
||||
"<th>Fehlgeschlagen</th><th>Anforderungen</th></tr>",
|
||||
"<h2>Per Test Suite</h2>",
|
||||
"<table><tr><th>Suite</th><th>Count</th><th>Passed</th>"
|
||||
"<th>Failed</th><th>Requirements</th></tr>",
|
||||
]
|
||||
for r in results:
|
||||
reqs = ", ".join(reqs_for(r["binary"])) or "—"
|
||||
|
||||
+21
-21
@@ -1,17 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Traceability-Werkzeug fuer demo-epb.
|
||||
Traceability tool for demo-epb.
|
||||
|
||||
Liest Markdown-Items aus safety/sg, reqs/sys, reqs/swe, arch/sys, arch/swe und
|
||||
verifiziert die Traceability-Kette:
|
||||
Reads Markdown items from safety/sg, reqs/sys, reqs/swe, arch/sys, arch/swe and
|
||||
verifies the traceability chain:
|
||||
|
||||
SG <-- SYS <-- SA
|
||||
<-- SWE <-- SWA <-- Code (@arch)
|
||||
<-- Tests (@reqs)
|
||||
|
||||
Subcommands:
|
||||
check Validiert Konsistenz, exit 1 bei Fehlern
|
||||
publish DIR Schreibt HTML + JSON nach DIR/
|
||||
check Validates consistency, exits 1 on errors
|
||||
publish DIR Writes HTML + JSON to DIR/
|
||||
|
||||
Run:
|
||||
python3 tools/traceability.py check
|
||||
@@ -160,27 +160,27 @@ def check_code_test_mapping(items: dict[str, Item]) -> list[str]:
|
||||
src = REPO / src_rel
|
||||
arch_tags, _ = extract_tags(src)
|
||||
if swa_id not in arch_tags:
|
||||
errors.append(f"{src_rel}: header @arch enthaelt {swa_id} nicht "
|
||||
f"(gefunden: {arch_tags or '—'})")
|
||||
errors.append(f"{src_rel}: header @arch does not contain {swa_id} "
|
||||
f"(found: {arch_tags or '—'})")
|
||||
|
||||
# For each test, verify @reqs covers the SWE that the corresponding SWA links to
|
||||
for test_file, swa_id in IMPLEMENTED_TESTS.items():
|
||||
test_path = REPO / "tests" / "unit" / test_file
|
||||
_, reqs_in_test = extract_tags(test_path)
|
||||
if not reqs_in_test:
|
||||
errors.append(f"tests/unit/{test_file}: kein @reqs Tag im Header")
|
||||
errors.append(f"tests/unit/{test_file}: no @reqs tag in header")
|
||||
continue
|
||||
swa = items.get(swa_id)
|
||||
if swa is None:
|
||||
errors.append(f"tests/unit/{test_file}: referenziertes "
|
||||
f"{swa_id} nicht gefunden")
|
||||
errors.append(f"tests/unit/{test_file}: referenced "
|
||||
f"{swa_id} not found")
|
||||
continue
|
||||
swa_swe = set(swa.links)
|
||||
test_swe = set(reqs_in_test)
|
||||
missing = swa_swe - test_swe
|
||||
if missing:
|
||||
errors.append(f"tests/unit/{test_file}: deckt nicht alle SWE "
|
||||
f"der {swa_id} ab — fehlend: {sorted(missing)}")
|
||||
errors.append(f"tests/unit/{test_file}: does not cover all SWE of "
|
||||
f"{swa_id} — missing: {sorted(missing)}")
|
||||
|
||||
return errors
|
||||
|
||||
@@ -241,7 +241,7 @@ def cmd_check(items: dict[str, Item]) -> int:
|
||||
for e in errors:
|
||||
print(f" - {e}")
|
||||
return 1
|
||||
print("OK — Traceability vollstaendig (SG → SYS → SA, SWE → SWA → Code+Test).")
|
||||
print("OK — Traceability complete (SG → SYS → SA, SWE → SWA → Code+Test).")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -314,7 +314,7 @@ def cmd_publish(items: dict[str, Item], out_dir: Path) -> int:
|
||||
".missing{color:#c00}",
|
||||
"</style></head><body>",
|
||||
"<h1>demo-epb — Traceability Matrix</h1>",
|
||||
"<p>Vollstaendige Kette: <code>SG → SYS → SA, SWE → SWA → Code (@arch) + Test (@reqs)</code></p>",
|
||||
"<p>Complete chain: <code>SG → SYS → SA, SWE → SWA → Code (@arch) + Test (@reqs)</code></p>",
|
||||
"<p>",
|
||||
]
|
||||
for p, _, label in SOURCES:
|
||||
@@ -325,9 +325,9 @@ def cmd_publish(items: dict[str, Item], out_dir: Path) -> int:
|
||||
|
||||
parts.append("<table>")
|
||||
parts.append(
|
||||
"<tr><th>Safety Goal</th><th>System-Requirement</th>"
|
||||
"<th>System-Arch</th><th>Software-Req</th>"
|
||||
"<th>Software-Arch</th><th>Code</th><th>Test</th></tr>"
|
||||
"<tr><th>Safety Goal</th><th>System Requirement</th>"
|
||||
"<th>System Arch</th><th>Software Req</th>"
|
||||
"<th>Software Arch</th><th>Code</th><th>Test</th></tr>"
|
||||
)
|
||||
|
||||
def cell_items(ids: list[str]) -> str:
|
||||
@@ -377,8 +377,8 @@ def cmd_publish(items: dict[str, Item], out_dir: Path) -> int:
|
||||
parts.append("</table>")
|
||||
|
||||
# Code/Test details
|
||||
parts.append("<h2>Code → Architektur</h2>")
|
||||
parts.append("<table><tr><th>Datei</th><th>@arch</th><th>@reqs</th></tr>")
|
||||
parts.append("<h2>Code → Architecture</h2>")
|
||||
parts.append("<table><tr><th>File</th><th>@arch</th><th>@reqs</th></tr>")
|
||||
for swa_id, src_rel in IMPLEMENTED_SWA.items():
|
||||
arch, reqs = extract_tags(REPO / src_rel)
|
||||
parts.append(
|
||||
@@ -388,8 +388,8 @@ def cmd_publish(items: dict[str, Item], out_dir: Path) -> int:
|
||||
)
|
||||
parts.append("</table>")
|
||||
|
||||
parts.append("<h2>Test → Anforderungen</h2>")
|
||||
parts.append("<table><tr><th>Test-Datei</th><th>Decklt SWA</th><th>@reqs</th></tr>")
|
||||
parts.append("<h2>Test → Requirements</h2>")
|
||||
parts.append("<table><tr><th>Test file</th><th>Covers SWA</th><th>@reqs</th></tr>")
|
||||
for test_file, swa_id in IMPLEMENTED_TESTS.items():
|
||||
_, reqs = extract_tags(REPO / "tests" / "unit" / test_file)
|
||||
parts.append(
|
||||
|
||||
Reference in New Issue
Block a user