You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
110 lines
4.4 KiB
110 lines
4.4 KiB
import 'package:eaimar/devices/bes/bes_protocol.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
/// 恒玄协议金测试:帧全部来自真机抓包或厂商文档样例,**一个字节都不能改**。
|
|
/// 这些测试红了,先怀疑改动、别怀疑帧。
|
|
void main() {
|
|
List<int> 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);
|
|
});
|
|
});
|
|
}
|
|
|