AI_UIAutomation/scripts/gen-p0-report.py

98 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
P0 测试报告整理输出 —— 最新计数口径(2026-06-23 与用户确认)。
用法: python3 scripts/gen-p0-report.py [日志路径] [输出md路径]
默认: reports/p0-all.log -> reports/P0_Android_真实结果.md
计数规则(口径):
1) 只取**最后一轮**(日志中最后一个 "P0 全量回归开始")到结尾的片段。
2) 添加用例(名含"添加"):按**用例最终结果**计 —— 重试后最终 PASS 记通过,并标注"第N次成功";
全程未 PASS 才记真失败。(添加用例以最后结果为准,重试成功不计失败)
3) 控制/其他用例:**BLE 与 WiFi 各算一条,不去重**(含 vitest 重试占位中间态),按执行条数计。
4) 失败按类分组:灯/卡片控制 / 设备控制(其他) / 场景·自动化 / 平台·账号。
本脚本只生成**数据底稿**(reports/P0_Android_真实结果.md)。最终交付报告请按
docs/测试报告_P0过夜回归_*.md 的格式人工汇总到 docs/测试报告_P0回归_YYYY-MM-DD.md
(总览/添加/控制/灯类/其他/已修复/结论与待办)。提示词见 memory: reference-p0-report-counting。
"""
import re, sys
from collections import defaultdict, Counter, OrderedDict
LOG = sys.argv[1] if len(sys.argv) > 1 else 'reports/p0-all.log'
OUT = sys.argv[2] if len(sys.argv) > 2 else 'reports/P0_Android_真实结果.md'
lines = open(LOG, encoding='utf-8', errors='ignore').read().splitlines()
starts = [i for i, l in enumerate(lines) if 'P0 全量回归开始' in l]
start = max(starts) if starts else 0
seg = lines[start:]
hdr = lines[start].strip() if starts else '(未找到回归开始标记,取全量)'
rows = [] # (status, name) 按出现顺序
for l in seg:
m = re.search(r'[✓✗○] \[(PASS|FAIL|SKIP)\]\s*(.+?)\s*\(([\d.]+)s\)', l)
if m:
rows.append((m.group(1), re.sub(r'\s+', ' ', m.group(2)).strip()))
# --- 添加用例:按用例最终结果归并 ---
add_seq = OrderedDict()
for s, n in rows:
if '添加' in n:
add_seq.setdefault(n, []).append(s)
add_first, add_retry, add_fail = [], [], []
for n, sts in add_seq.items():
if 'PASS' in sts:
fb = 0
for s in sts:
if s == 'PASS':
break
fb += 1
(add_retry if fb > 0 else add_first).append((n, fb))
else:
add_fail.append(n)
# --- 非添加:按执行计数(不去重) ---
def cat(n):
if '场景' in n or '自动化' in n: return '场景/自动化'
if any(k in n for k in ['登录','登出','注册','注销','忘记','第三方','feedback','插件','房间','消息']): return '平台/账号'
if '首页卡片' in n or '首页找到' in n: return '灯/卡片控制'
return '设备控制(其他)'
nonadd = [(s, n) for s, n in rows if '添加' not in n]
fg = defaultdict(Counter)
for s, n in nonadd:
if s == 'FAIL': fg[cat(n)][n] += 1
na_pass = sum(1 for s, n in nonadd if s == 'PASS')
na_fail = sum(1 for s, n in nonadd if s == 'FAIL')
na_skip = sum(1 for s, n in nonadd if s == 'SKIP')
o = []
o.append('# Android P0 真实结果\n')
o.append(f'> 来源:{hdr}(最后一轮)。\n')
o.append('> **计数口径**:① 添加用例按"用例最终结果"计,**重试后成功记通过**(标注第几次成功)、全程未过才记失败;'
'② 控制/其他用例 **BLE 与 WiFi 各算一条,不去重**(含 vitest 重试占位中间态)。\n\n')
add_total, add_ok = len(add_seq), len(add_first) + len(add_retry)
o.append('## 汇总\n')
o.append(f'- **添加(按用例):{add_total} 个 → ✅通过 {add_ok}(其中重试后成功 {len(add_retry)})· ❌失败 {len(add_fail)}**\n')
o.append(f'- **控制/其他(按执行):✅PASS {na_pass} · ❌FAIL {na_fail} · ⏭SKIP {na_skip}**\n\n')
o.append(f'## 添加用例({add_total})\n')
o.append(f'\n### 🔁 重试后成功({len(add_retry)})— 计通过,稳定性需关注\n')
for n, f in sorted(add_retry, key=lambda x: -x[1]):
o.append(f'- {n} — **第 {f+1} 次成功**(前 {f} 次失败)\n')
o.append(f'\n### ❌ 真失败({len(add_fail)})\n')
for n in add_fail: o.append(f'- {n}(全程未成功)\n')
o.append(f'\n### ✅ 首次即通过({len(add_first)})\n')
for n, _ in add_first: o.append(f'- {n}\n')
o.append(f'\n## 控制/其他 失败明细(FAIL {na_fail} 条;`×N`=含重试占位)\n')
for c in sorted(fg, key=lambda k: -sum(fg[k].values())):
sub = fg[c]
o.append(f'\n### {c}(失败 {sum(sub.values())} 条)\n')
for n, cnt in sub.most_common():
o.append(f'- {n}' + (f' ×{cnt}\n' if cnt > 1 else '\n'))
open(OUT, 'w', encoding='utf-8').write(''.join(o))
print(f'已生成 {OUT}')
print(f'添加 {add_total}: 首通{len(add_first)} 重试成功{len(add_retry)} 真失败{len(add_fail)} | '
f'非添加 PASS{na_pass} FAIL{na_fail} SKIP{na_skip}')