{%- set status_label = {'PASS':'通过','FAIL':'失败','ERROR':'出错','SKIPPED':'跳过','BLOCKED':'被阻塞','GAP':'缺口','UNVERIFIED':'未判定'} %}
{%- set nb = against_baseline or {} %}
{%- set nr = against_run or {} %}
{%- set newly_base = nb.get('newly_failing', []) %}
{%- set newly_run = nr.get('newly_failing', []) %}
{{ title }}
{{ run_id or 'run' }}
{%- if selector %}选择器 {{ selector }}{% endif %}
{%- if env %}环境 {{ env }}{% endif %}
{{ generated }}耗时 {{ '%.2f'|format(duration) }} s
{%- for cf in merge_conflicts %}{% set c = case_by_id.get(cf.nodeid) %}
{{ status_label.get(cf.status, cf.status) }}{{ c.title or c.name if c else cf.nodeid }}{% for seen in cf.seen %}{{ seen.run_id }}:{{ status_label.get(seen.status, seen.status) }}{% if not loop.last %} · {% endif %}{% endfor %}
{%- if c.nodeid in newly_base %}新红
{%- elif c.nodeid in nb.get('no_passing_record', []) %}基线无通过记录{% endif %}
{%- if c.nodeid in newly_run %} 比上次新失败{% endif %}
{%- for src, n in by_source.items() %}
{%- set gp = n.get('gap', 0) %}
{{ src }}
{%- if n.passed %}{% endif %}
{%- if n.failed %}{% endif %}
{%- if gp %}{% endif %}
{{ n.passed }} 成立{% if n.failed %} · {{ n.failed }} 不成立{% endif %}{% if gp %} · {{ gp }} 缺口{% endif %}
{%- endfor %}
{%- endif %}
{%- endif %}
用例明细
{%- for suite, suite_cases in by_suite.items() %}
{{ suite }}
{%- for case in suite_cases %}
{%- set bad = case.status in ('FAIL', 'ERROR') %}
{{ status_label.get(case.status, case.status) }}{{ case.title or case.name }}{{ case.nodeid or case.name }}
{%- for key, value in case.dims.items() %}{{ key }}={{ value }}{% endfor %}
{%- if case.judgements %}{{ case.checks_passed }} / {{ case.judgements|length }} 成立{% endif %}
{{ '%.3f'|format(case.duration) }} s
{%- if case.reason %}
{{ category_label.get(case.category, case.category or status_label.get(case.status, case.status)) }}{% if case.confidence %} · {{ confidence_label.get(case.confidence, case.confidence) }}{% endif %}{{ case.reason }}
{%- if case.next_step %}下一步:{{ case.next_step }}{% endif %}
{%- for check in case.checks|sort(attribute='ok') %}
{%- set cls = 'gap' if check.kind == 'gap' else ('ok' if check.ok else 'bad') %}
{{ '缺口' if cls == 'gap' else ('成立' if check.ok else '不成立') }}
{{ check.name }}
{{ check.expr or '—' }}
{{ check.src }}
{%- endfor %}
{%- elif case.status == 'PASS' %}
这条用例通过了,但没有留下任何判定。
{%- endif %}
{%- if case.faults %}
这条用例注入了故障:{% for f in case.faults %}{{ f.description }}{% if f.started and f.ended %}({{ '%.1f'|format(f.ended - f.started) }} 秒){% endif %}{% if not loop.last %};{% endif %}{% endfor %}。
{%- endif %}
{%- set visual = case.artifacts|selectattr('kind', 'in', ['golden', 'actual', 'diff'])|list %}
{%- for group in visual|map(attribute='group')|unique %}
图像判定 · {{ group }}
{%- for kind, ttl in [('golden', '基准图'), ('actual', '本次'), ('diff', '差异(红框为变化位置)')] %}
{%- for a in visual if a.group == group and a.kind == kind %}
{{ ttl }}{% if a.src %}{% else %}{{ a.path }}{% endif %}
{%- endfor %}
{%- endfor %}
{%- endfor %}
{%- set histories = case.artifacts|selectattr('kind', 'in', ['history', 'checker-result'])|list %}
{%- if histories %}
操作历史
{%- for a in histories %}
{%- if a.kind == 'history' %}
{{ a.group }} 模型的历史(EDN),复查{{ a.command }}
{%- else %}
检查器的完整结论{{ a.path }}
{% endif %}
{%- endfor %}
{%- endif %}
{%- set others = case.artifacts|rejectattr('kind', 'in', ['golden', 'actual', 'diff', 'history', 'checker-result'])|list %}
{%- if others %}
失败现场
{%- for a in others %}
{%- if a.kind == 'screenshot' and a.src %}
{%- elif a.kind == 'screenshot' %}
{%- endif %}
{%- set traces = case.traces %}
{%- if traces %}
链路(服务端的调用记录):{% for t in traces %}{% if t.url|web_url %}{{ t.id }}{% else %}{{ t.id }}{% endif %}{% if not loop.last %}、{% endif %}{% endfor %}
{%- endif %}
{%- if case.exchanges %}
{%- set total_ms = case.exchange_ms or 1 %}
{%- set kinds = case.exchanges|map(attribute='protocol')|map('default', '')|unique|list %}
{% for k in kinds %}{{ protocol_names.get(k, k) }}{{ ' / ' if not loop.last }}{% endfor %} · {{ case.exchanges|length }} 次请求 · {{ case.exchange_ms|ms }}
{%- for ex in case.exchanges %}
{%- set grpc = ex.protocol == 'grpc' %}
{%- set kafka = ex.protocol == 'kafka' %}
{%- set other = ex.protocol in ok_reasons %}
{#- no reason: not ended when the case did -- a stream the test stopped reading, a message not yet acknowledged #}
{%- set broke = ex.error or ((ex.reason and ex.reason not in ok_reasons[ex.protocol]) if other else not (ex.status and 200 <= ex.status < 400)) %}
{{ ex.method or 'GET' }}{{ ex.url }}
{%- if ex.identity %}{{ ex.identity }}{% endif %}
{%- if ex.role == 'login' %}登录{% elif ex.role == 'replayed' %}未登录被拒,重新登录后重放{% endif %}
{{ (ex.reason or '未结束') if other else (ex.status or '—') }}{{ ex.ms|ms }}
{%- if ex.error %}
{{ ex.error }}
{% endif %}
{{ ex.method or 'GET' }}{{ ex.url }}
{%- if other %}{{ ex.reason or '未结束' }}
{%- else %}
{%- if ex.status %}{{ ex.status }} {{ ex.reason }}{% endif %}
{%- endif %}