AI_UIAutomation/scripts/sim-p0-writeback.ts

107 lines
6.6 KiB
TypeScript

import { ANCHORS, onesCtrlByCard } from '../utils/common/ones-anchor.helper';
import * as fs from 'fs';
import * as path from 'path';
// 模拟"现有 P0 用例回写"覆盖:枚举所有能 emit 的锚点(case号 + 15975/15974 step uuid),
// 与 plan 全量(cases + 49 ble + 56 wifi)对比,列出**没有任何用例锚点覆盖的**(漏)。
const wb = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../test-plan/ones-writeback-params.json'), 'utf-8'));
const emittedCases = new Set<number>(); // add/feature/platform case numbers
const emittedBle = new Set<string>(); // 15975 step uuids
const emittedWifi = new Set<string>(); // 15974 step uuids
// 1) ANCHORS 全量:add(case) + ctrlBle(15975) + ctrlWifi(15974)
for (const cat of Object.keys(ANCHORS)) {
for (const dev of Object.keys(ANCHORS[cat])) {
const a = ANCHORS[cat][dev];
if (a.add) emittedCases.add(a.add);
if (a.ctrlBle) emittedBle.add(a.ctrlBle);
if (a.ctrlWifi) emittedWifi.add(a.ctrlWifi);
}
}
// 2) 灯类卡片名(P0 实际跑的灯)
const LIGHT_CARDS = ['Ceiling Light Pro', 'RGBWW Floor Lamp', 'RGBICWW Floor Lamp', 'RGBWW Strip Light 3',
'RGBIC Neon Rope Light', 'Candle Warmer Lamp', 'Color Bulb', 'Strip Light SB', 'Permanent Outdoor Lights'];
for (const card of LIGHT_CARDS) {
process.env.PROTO = 'ble'; const b = onesCtrlByCard(card);
process.env.PROTO = 'wifi'; const w = onesCtrlByCard(card);
const mb = b.match(/15975#(\w+)/); if (mb) emittedBle.add(mb[1]);
const mw = w.match(/15974#(\w+)/); if (mw) emittedWifi.add(mw[1]);
}
// 3) 特殊锚点:bot / meter报警校准(onesBleStep) / air_purifier 双发 / platform
emittedBle.add('6e4uZSVe'); emittedWifi.add('Qt4hcgB4'); // bot
['NvpUv9Tt', '3Jm5FAdW', 'QfYLdj8s'].forEach(u => emittedBle.add(u)); // meter 报警/校温/校湿
['7qxA1ajE', 'R5PDp8BR'].forEach(u => emittedBle.add(u)); // air_purifier 美规/日规
// meter 家族报警/校准(P0 按 METER_*_STEP env 参数化跑 meter_alert_calibration):MeterPlus/OutdoorMeter/MeterPro
['UZCtZRPy', '9mheLMDJ', 'GSZv8BYm', 'QwnZJARA', 'Ncg7BDeR', 'k6cqKqtZ', 'JggzNXDo', 'L967RGHa', '8Wzaj5pB'].forEach(u => emittedBle.add(u));
[15940, 15941, 15942, 15943, 15944, 15945, 15976, 227290, 298968].forEach(n => emittedCases.add(n)); // platform
// 3b) 扫所有测试文件的**字面锚点**(捕获硬编码 [ONES:号] / [ONES:15975#step] / [ONES:15974#step],如 bot/color_bulb/urc/meter_control)
function walk(dir: string): string[] {
const out: string[] = [];
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
const p = path.join(dir, e.name);
if (e.isDirectory()) out.push(...walk(p));
else if (e.name.endsWith('.test.ts')) out.push(p);
}
return out;
}
for (const file of walk(path.resolve(__dirname, '../tests'))) {
const src = fs.readFileSync(file, 'utf-8');
for (const m of src.matchAll(/ONES:(\d+)#(\w+)/g)) {
// step 锚点:15975→ble / 15974→wifi
if (m[1] === '15975') emittedBle.add(m[2]);
else if (m[1] === '15974') emittedWifi.add(m[2]);
}
for (const m of src.matchAll(/ONES:(\d+)(?!#)/g)) emittedCases.add(parseInt(m[1], 10)); // 纯 case 号
}
// 4) diff
const planCases: any[] = wb.cases;
const bleSteps: string[] = wb.controlCases['15975'].steps; // uuid 字符串数组
const wifiSteps: string[] = wb.controlCases['15974'].steps;
// 表头 49 步 ble uuid→名(2026-06-11 核对),供可读输出
const BLE_NAME: Record<string, string> = {
'6e4uZSVe': 'Bot', '5YEfmpJz': 'Lock', '774V8bND': 'Curtain', 'NvpUv9Tt': 'Meter报警', '3Jm5FAdW': 'Meter校温',
'QfYLdj8s': 'Meter校湿', 'GkkrZpaC': 'Humidifier', 'TNhSAuUC': 'Bulb', '4poyiVwD': 'StripLight', '2528azD6': '吸顶灯pro',
'V15M64cP': '吸顶灯lte', '2jSFfY4k': 'PlugMini', 'UZCtZRPy': 'MeterPlus报警', '9mheLMDJ': 'MeterPlus校温', 'GSZv8BYm': 'MeterPlus校湿',
'HrigrD2X': 'Motion', 'A8C4BsCm': 'Contact', '3frMRDZj': 'BlindTilt-V20', 'Qbm9VB72': 'BlindTilt-V12', 'QwnZJARA': 'IOSensor报警',
'Ncg7BDeR': 'IOSensor校温', 'k6cqKqtZ': 'IOSensor校湿', 'QqsnPMc3': 'Curtain3', 'CiFPEbNS': 'Battery循环扇', 'LnPNEJmS': 'Lock Pro',
'3PmZMk1s': 'Humidifier2', 'JBszgxRS': '无电池循环扇', 'SmzuHtWi': 'RollerShade', '7qxA1ajE': '美规净化器', 'R5PDp8BR': '日规Table净化器',
'3wM6fW8W': 'Curtain3-2025', 'JggzNXDo': 'MeterPro报警', 'L967RGHa': 'MeterPro校温', '8Wzaj5pB': 'MeterPro校湿', '3bYsmiZG': 'MeterProCO2报警',
'NDqRQxVx': 'CO2校温', 'DxKG9Acs': 'CO2校湿', '3ob75cjf': 'RGBWW灯带3', 'JVnRz7xY': 'RGBWW落地灯', 'MKgLq1oy': 'Relay1pm',
'A1TLxsFw': 'Relay1', 'PG62s725': 'Relay2pm开关', 'AafJ5tj5': 'Relay2pm覆盖', 'E5YRm4Pg': 'GarageDoor', 'QAHNrXrm': 'RGBICWW灯带',
'AfxmoAEq': 'RGBICWW落地灯', 'E2QCS8xy': 'RGBIC霓虹钢丝', 'TSVKWcJ3': 'eu plug', 'XWmNA9UL': 'Standing循环扇',
};
const missingCases = planCases.filter(c => !emittedCases.has(c.number));
const missingBle = bleSteps.filter(u => !emittedBle.has(u));
const missingWifi = wifiSteps.filter(u => !emittedWifi.has(u));
console.log(`\n===== 模拟 P0 回写覆盖 (plan ${wb.defaultPlan}) =====`);
console.log(`cases: ${planCases.length} 计划 / ${emittedCases.size} 锚点 → 漏 ${missingCases.length}`);
console.log(`ble step(15975): ${bleSteps.length} 计划 / 命中 ${bleSteps.length - missingBle.length} → 漏 ${missingBle.length}`);
console.log(`wifi step(15974): ${wifiSteps.length} 计划 / 命中 ${wifiSteps.length - missingWifi.length} → 漏 ${missingWifi.length}`);
console.log(`\n--- 漏的 cases(add/feature/platform,无任何用例锚点)---`);
for (const c of missingCases) console.log(` ONES:${c.number} ${c.name}`);
console.log(`\n--- 漏的 BLE 控制 step(15975)---`);
for (const u of missingBle) console.log(` ${u} ${BLE_NAME[u] || '(未知,表头无此uuid)'}`);
console.log(`\n--- 漏的 WiFi 控制 step(15974)(共 ${missingWifi.length},uuid)---`);
console.log(' ' + missingWifi.join(' '));
// 生成合成 .results.json(所有 emit 的锚点都 PASS),供 sync-ones-results --dry-run 用真实匹配代码验证反写。
if (process.argv.includes('--emit-results')) {
const results: any[] = [];
emittedCases.forEach(n => results.push({ suite: 'sim', name: `[P0][ONES:${n}] sim`, status: 'PASS', duration: 1, detail: 'sim' }));
emittedBle.forEach(u => results.push({ suite: 'sim', name: `[P0][ONES:15975#${u}][ble] sim`, status: 'PASS', duration: 1, detail: 'sim' }));
emittedWifi.forEach(u => results.push({ suite: 'sim', name: `[P0][ONES:15974#${u}][wifi] sim`, status: 'PASS', duration: 1, detail: 'sim' }));
const out = '/tmp/sim-results.json';
fs.writeFileSync(out, JSON.stringify({ results }, null, 2));
console.log(`\n[--emit-results] 已写合成结果 ${results.length} 条 → ${out}`);
}