AI_UIAutomation/scripts/gen-3state-report.py

191 lines
7.1 KiB
Python

#!/usr/bin/env python3
"""
三态控制成功率对比报告。
用法:
python3 scripts/gen-3state-report.py <输出md> \
--android-both reports/ctrl3-android-both.log \
--android-split reports/ctrl3-android-split.log \
--ios-both reports/ctrl3-ios-both.log \
--ios-split reports/ctrl3-ios-split.log
状态映射(与 run-ctrl-3states.sh / 老脚本一致):
both 日志 → 状态「BLE+WiFi」:计 `控制:单轮` 标记之后的控制用例。
split 日志 → 状态「仅BLE」(PROTO=ble 段)+「仅WiFi」(PROTO=wifi 段)。
口径(对齐 gen-p0-report.py):同一段内同名用例取**最后一次结果**去重(消除 vitest 重试占位),
成功率 = 通过 / (通过 + 失败),分母**剔除 SKIP**(SKIP 不算有效样本)。
"""
import re, sys
RESULT_RE = re.compile(r'[✓✗○] \[(PASS|FAIL|SKIP)\]\s*(.+?)\s*\(([\d.]+)s\)')
def parse_phase(path, phase):
"""phase ∈ {'both','ble','wifi'};返回 (通过, 失败, 跳过, 明细dict{name:last_status})。"""
if not path:
return None
try:
lines = open(path, encoding='utf-8', errors='ignore').read().splitlines()
except FileNotFoundError:
return None
cur = None # 当前所处段
seq = {} # name -> [status...] (保序)
order = []
for l in lines:
if '控制:单轮' in l:
cur = 'both'
elif '控制 PROTO=ble' in l:
cur = 'ble'
elif '控制 PROTO=wifi' in l:
cur = 'wifi'
elif '平台 + 账号阶段' in l:
cur = 'tail'
m = RESULT_RE.search(l)
if m and cur == phase:
name = re.sub(r'\s+', ' ', m.group(2)).strip()
if '添加' in name: # 控制态里理论上没有添加;保险起见剔除
continue
if name not in seq:
seq[name] = []
order.append(name)
seq[name].append(m.group(1))
p = f = sk = 0
detail = {}
for name in order:
last = seq[name][-1]
detail[name] = last
if last == 'PASS': p += 1
elif last == 'FAIL': f += 1
else: sk += 1
return (p, f, sk, detail)
def rate(p, f):
return f'{100 * p / (p + f):.1f}%' if (p + f) else 'N/A'
def main():
out = sys.argv[1]
argmap = {}
args = sys.argv[2:]
i = 0
while i < len(args):
if args[i].startswith('--'):
argmap[args[i][2:]] = args[i + 1]
i += 2
else:
i += 1
# (平台, 状态标签, 日志key, 段)
plan = [
('Android', 'BLE+WiFi', 'android-both', 'both'),
('Android', '仅BLE', 'android-split', 'ble'),
('Android', '仅WiFi', 'android-split', 'wifi'),
('iOS', 'BLE+WiFi', 'ios-both', 'both'),
('iOS', '仅BLE', 'ios-split', 'ble'),
('iOS', '仅WiFi', 'ios-split', 'wifi'),
]
results = {} # (平台,状态) -> (p,f,sk,detail) or None
for plat, state, key, phase in plan:
results[(plat, state)] = parse_phase(argmap.get(key), phase)
o = []
o.append('# 三态控制成功率对比(BLE+WiFi / 仅BLE / 仅WiFi)\n\n')
o.append('> 口径:同一状态内同名用例取最后一次结果去重(消除 vitest 重试占位);'
'**成功率 = 通过 /(通过+失败),分母剔除 SKIP**。\n')
o.append('> 状态来源:both 日志=BLE+WiFi(两 radio 都开);'
'split 日志=仅BLE(关WiFi/开蓝牙)+ 仅WiFi(开WiFi/关蓝牙)。\n\n')
# ---- 主表:每平台每状态一行 ----
o.append('## 汇总对比\n\n')
o.append('| 平台 | 状态 | 通过 | 失败 | 跳过 | 成功率(剔除SKIP) |\n')
o.append('|---|---|---|---|---|---|\n')
for plat, state in [(p, s) for p in ('Android', 'iOS') for s in ('BLE+WiFi', '仅BLE', '仅WiFi')]:
r = results[(plat, state)]
if r is None:
o.append(f'| {plat} | {state} | — | — | — | 无数据/未跑 |\n')
else:
p, f, sk, _ = r
o.append(f'| {plat} | {state} | {p} | {f} | {sk} | {rate(p, f)} |\n')
o.append('\n')
# ---- 每平台三态横向对比 ----
for plat in ('Android', 'iOS'):
o.append(f'## {plat} 三态横向对比\n\n')
o.append('| 状态 | 有效样本(通过+失败) | 成功率 |\n|---|---|---|\n')
for state in ('BLE+WiFi', '仅BLE', '仅WiFi'):
r = results[(plat, state)]
if r is None:
o.append(f'| {state} | — | 无数据 |\n')
else:
p, f, sk, _ = r
o.append(f'| {state} | {p + f} | {rate(p, f)} |\n')
o.append('\n')
# ---- 各状态失败明细 + 跨态差异(哪些用例只在某态失败)----
o.append('## 失败明细(按平台/状态)\n\n')
for plat in ('Android', 'iOS'):
for state in ('BLE+WiFi', '仅BLE', '仅WiFi'):
r = results[(plat, state)]
if r is None:
continue
p, f, sk, detail = r
fails = [n for n, s in detail.items() if s == 'FAIL']
skips = [n for n, s in detail.items() if s == 'SKIP']
o.append(f'### {plat} · {state}(FAIL {len(fails)} · SKIP {len(skips)})\n')
if fails:
for n in fails:
o.append(f'- ❌ {n}\n')
else:
o.append('- (无失败)\n')
o.append('\n')
# ---- 跨态差异分析:同平台内,某用例在不同态结果不一致的挑出来 ----
o.append('## 跨态差异(同平台同用例在不同连接态结果不一致)\n\n')
for plat in ('Android', 'iOS'):
states = ['BLE+WiFi', '仅BLE', '仅WiFi']
details = {}
any_data = False
for st in states:
r = results[(plat, st)]
if r is not None:
details[st] = r[3]
any_data = True
if not any_data:
continue
# 收集所有用例名
allnames = set()
for d in details.values():
allnames.update(d.keys())
diffs = []
for n in sorted(allnames):
row = [details.get(st, {}).get(n, '·') for st in states]
if len({x for x in row if x != '·'}) > 1: # 出现过 ≥2 种不同结果
diffs.append((n, row))
o.append(f'### {plat}\n')
if diffs:
o.append('| 用例 | BLE+WiFi | 仅BLE | 仅WiFi |\n|---|---|---|---|\n')
for n, row in diffs:
o.append(f'| {n} | {row[0]} | {row[1]} | {row[2]} |\n')
else:
o.append('- (无差异,或数据不足以对比)\n')
o.append('\n')
open(out, 'w', encoding='utf-8').write(''.join(o))
print(f'已生成 {out}')
# 控制台速览
for plat in ('Android', 'iOS'):
parts = []
for state in ('BLE+WiFi', '仅BLE', '仅WiFi'):
r = results[(plat, state)]
if r is None:
parts.append(f'{state}:无数据')
else:
p, f, sk, _ = r
parts.append(f'{state}:{p}/{p+f}({rate(p, f)})')
print(f'{plat} ' + ' '.join(parts))
if __name__ == '__main__':
main()