import 'package:eaimar/devices/bes/bes_protocol.dart'; import 'package:flutter_test/flutter_test.dart'; /// 恒玄协议金测试:帧全部来自真机抓包或厂商文档样例,**一个字节都不能改**。 /// 这些测试红了,先怀疑改动、别怀疑帧。 void main() { List h(String s) => s .trim() .split(RegExp(r'\s+')) .where((e) => e.isNotEmpty) .map((e) => int.parse(e, radix: 16)) .toList(); group('BB 0A 状态帧', () { test('Echo-one 0.0.2 真机帧:不在盒内,无属性 3,帧尾 MAC 逐字节吻合', () { // 2026-09-01 真机抓帧(属性 6 那 12 字节按实测样本填) final frame = h('BB 0A 25 ' '02 01 45 ' '02 02 56 ' '02 04 49 ' '02 05 6C ' '0D 06 00 01 01 70 B3 B1 4D 33 DC 00 00 00 ' '07 07 11 11 22 33 33 A4'); final st = BesProtocol.parseStatus(frame); expect(st.batteryLeft, 69); expect(st.batteryRight, 86); expect(st.batteryCase, isNull, reason: '不在盒内时没有属性 3'); expect(st.chargingLeft, isFalse); expect(st.chargingRight, isFalse); expect(st.mac, '11:11:22:33:33:A4'); expect(st.summaryBattery, 69, reason: '左右取较低一侧'); }); test('厂商文档样例:三个电量都在', () { final frame = h('BB 0A 28 02 01 2B 02 02 18 02 03 64 02 04 85 02 05 6E'); final st = BesProtocol.parseStatus(frame); expect(st.batteryLeft, 43); expect(st.batteryRight, 24); expect(st.batteryCase, 100); expect(st.summaryBattery, 24); }); test('最高位是充电指示位,必须 & 0x7F', () { final frame = h('BB 0A 09 02 01 C5 02 02 56'); final st = BesProtocol.parseStatus(frame); expect(st.batteryLeft, 69); expect(st.chargingLeft, isTrue); expect(st.batteryRight, 86); expect(st.chargingRight, isFalse); }); test('属性 4/5 的值超过 100 不会被当成电量', () { final frame = h('BB 0A 06 02 04 89 02 05 6E'); final st = BesProtocol.parseStatus(frame); expect(st.hasBattery, isFalse); }); test('从偏移 4 开始解(固件版本帧的写法)会一个值都解不出——守住起点是 3', () { final frame = h('BB 0A 25 02 01 45 02 02 56 02 04 49 02 05 6C'); // 人为错位一格模拟旧 bug:第一个「块长度」读到的是属性号 0x01 < 2 final shifted = [frame[0], frame[1], frame[2], 0x00, ...frame.sublist(3)]; expect(BesProtocol.parseStatus(shifted).hasBattery, isFalse); expect(BesProtocol.parseStatus(frame).hasBattery, isTrue); }); }); group('固件版本帧', () { test('BB 86 优先左耳,版本取块后 3 字节', () { final frame = h('BB 86 14 00 ' '09 01 00 00 00 00 00 00 00 02 ' '09 02 00 00 00 00 00 00 00 03'); final fw = BesProtocol.parseFirmware(frame); expect(fw.left, '0.0.2'); expect(fw.right, '0.0.3'); expect(fw.preferred, '0.0.2'); }); }); group('命令字节', () { test('每个 feature 对应的字节序列固定', () { expect(BesCmd.frame(BesCmd.firmwareVersion), [0xAA, 0x06]); expect(BesCmd.frame(BesCmd.status), [0xAA, 0x09]); expect(BesCmd.frame(BesCmd.callState), [0xAA, 0x0E]); expect(BesCmd.frame(BesCmd.callModeStart), [0xAA, 0x51]); expect(BesCmd.frame(BesCmd.callModeStop), [0xAA, 0x52]); expect(BesCmd.frame(BesCmd.asrStarted), [0xAA, 0x57]); expect(BesCmd.frame(BesCmd.asrStopped), [0xAA, 0x58]); expect(BesCmd.frame(BesCmd.micRecordStart), [0xAA, 0x59]); expect(BesCmd.frame(BesCmd.micRecordStop), [0xAA, 0x5A]); expect(BesCmd.frame(BesCmd.aiExit), [0xAA, 0x65]); expect(BesCmd.frame(BesCmd.faceToFaceStart), [0xAA, 0x66]); expect(BesCmd.frame(BesCmd.faceToFaceStop), [0xAA, 0x67]); expect(BesCmd.frame(BesCmd.aiAccept), [0xAA, 0x6A]); }); test('电量请求是 09 不是 0A,也不是 |0x80 推出来的任何值', () { expect(BesCmd.status, 0x09); expect(BesResp.status, 0x0A); expect(BesCmd.status | 0x80, isNot(BesResp.status)); }); test('0x51~0x6A 会切工作模式,探测时必须跳过', () { for (var c = 0x51; c <= 0x6A; c++) { expect(BesCmd.switchesMode(c), isTrue, reason: '0x${c.toRadixString(16)}'); } expect(BesCmd.switchesMode(0x09), isFalse); expect(BesCmd.switchesMode(0x06), isFalse); }); }); }