#!/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()