# -*- coding: utf-8 -*- """统一测试报告:PASS/FAIL/SKIP/SENT 四态 + Markdown 输出 + 退出码.""" from __future__ import annotations import os from dataclasses import dataclass, field from datetime import datetime STATUSES = ("PASS", "FAIL", "SKIP", "SENT") @dataclass class CaseResult: suite: str name: str status: str detail: str = "" @dataclass class HarnessReport: title: str = "K1 HIL 自动化测试报告" cases: list[CaseResult] = field(default_factory=list) notes: list[str] = field(default_factory=list) started: datetime = field(default_factory=datetime.now) def add(self, suite: str, name: str, status: str, detail: str = "") -> None: status = status.upper() assert status in STATUSES, f"bad status {status}" self.cases.append(CaseResult(suite, name, status, detail)) print(f"[{status:>4}] {suite}/{name}" + (f" | {detail}" if detail else ""), flush=True) def extend_rows(self, suite: str, rows) -> None: """吸收 test_protocol_app_sim.Results 风格的 (name, status, detail) 行.""" for name, status, detail in rows: self.add(suite, name, status, detail) def counts(self) -> dict: return {s: sum(1 for c in self.cases if c.status == s) for s in STATUSES} def exit_code(self) -> int: return 1 if self.counts()["FAIL"] else 0 def summary_text(self) -> str: n = self.counts() return f"PASS={n['PASS']} FAIL={n['FAIL']} SKIP={n['SKIP']} SENT={n['SENT']}" def print_summary(self) -> None: print("\n===== 汇总 =====", flush=True) for suite in dict.fromkeys(c.suite for c in self.cases): sub = [c for c in self.cases if c.suite == suite] n = {s: sum(1 for c in sub if c.status == s) for s in STATUSES} print(f" {suite:<12} PASS={n['PASS']} FAIL={n['FAIL']} SKIP={n['SKIP']} SENT={n['SENT']}", flush=True) print(f" {'TOTAL':<12} {self.summary_text()}", flush=True) for c in self.cases: if c.status == "FAIL": print(f" FAIL: {c.suite}/{c.name} | {c.detail}", flush=True) def write_markdown(self, path: str) -> str: os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True) lines = [ f"# {self.title}", "", f"- 时间: {self.started.isoformat(timespec='seconds')}", f"- 结果: {self.summary_text()}", ] for note in self.notes: lines.append(f"- {note}") lines += ["", "| 套件 | 用例 | 结果 | 详情 |", "|------|------|------|------|"] for c in self.cases: detail = c.detail.replace("|", "\\|").replace("\n", " ") lines.append(f"| {c.suite} | {c.name} | {c.status} | {detail} |") lines.append("") with open(path, "w", encoding="utf-8", newline="\n") as f: f.write("\n".join(lines)) print(f"报告已保存: {os.path.abspath(path)}", flush=True) return path