161 lines
4.7 KiB
Dart
161 lines
4.7 KiB
Dart
import 'package:abawo_bt_app/model/shifter_types.dart';
|
|
import 'package:abawo_bt_app/service/dfu_protocol.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
group('BootloaderDfuProtocol CRC32', () {
|
|
test('matches known vector', () {
|
|
final crc = BootloaderDfuProtocol.crc32('123456789'.codeUnits);
|
|
expect(crc, 0xCBF43926);
|
|
});
|
|
});
|
|
|
|
group('BootloaderDfuProtocol control payload encoding', () {
|
|
test('encodes START payload with exact 19-byte LE layout', () {
|
|
final payload = BootloaderDfuProtocol.encodeStartPayload(
|
|
const BootloaderDfuStartPayload(
|
|
totalLength: 0x1234,
|
|
imageCrc32: 0x89ABCDEF,
|
|
appStart: universalShifterDfuAppStart,
|
|
imageVersion: 0x10203040,
|
|
sessionId: 0x22,
|
|
flags: universalShifterDfuFlagNone,
|
|
),
|
|
);
|
|
|
|
expect(payload.length, 19);
|
|
expect(payload, [
|
|
universalShifterDfuOpcodeStart,
|
|
0x34,
|
|
0x12,
|
|
0x00,
|
|
0x00,
|
|
0xEF,
|
|
0xCD,
|
|
0xAB,
|
|
0x89,
|
|
0x00,
|
|
0x00,
|
|
0x03,
|
|
0x00,
|
|
0x40,
|
|
0x30,
|
|
0x20,
|
|
0x10,
|
|
0x22,
|
|
universalShifterDfuFlagNone,
|
|
]);
|
|
});
|
|
|
|
test('encodes FINISH, ABORT, and GET_STATUS payloads', () {
|
|
expect(
|
|
BootloaderDfuProtocol.encodeFinishPayload(0x12),
|
|
[universalShifterDfuOpcodeFinish, 0x12],
|
|
);
|
|
expect(
|
|
BootloaderDfuProtocol.encodeAbortPayload(0x34),
|
|
[universalShifterDfuOpcodeAbort, 0x34],
|
|
);
|
|
expect(
|
|
BootloaderDfuProtocol.encodeGetStatusPayload(),
|
|
[universalShifterDfuOpcodeGetStatus],
|
|
);
|
|
});
|
|
});
|
|
|
|
group('BootloaderDfuProtocol data frame building', () {
|
|
test('builds offset frames with payload CRC and variable final length', () {
|
|
final image = List<int>.generate(60, (index) => index);
|
|
final frames = BootloaderDfuProtocol.buildDataFrames(
|
|
imageBytes: image,
|
|
sessionId: 0x7A,
|
|
);
|
|
|
|
expect(frames.length, 2);
|
|
|
|
expect(frames[0].sessionId, 0x7A);
|
|
expect(frames[0].offset, 0);
|
|
expect(frames[0].payloadLength,
|
|
universalShifterBootloaderDfuMaxPayloadSizeBytes);
|
|
expect(frames[0].bytes.length, universalShifterDfuFrameSizeBytes);
|
|
expect(frames[0].bytes[0], 0x7A);
|
|
expect(frames[0].bytes.sublist(1, 5), [0, 0, 0, 0]);
|
|
expect(
|
|
frames[0].bytes.sublist(5, 9),
|
|
_leU32Bytes(BootloaderDfuProtocol.crc32(image.sublist(0, 55))),
|
|
);
|
|
expect(frames[0].bytes.sublist(9), image.sublist(0, 55));
|
|
|
|
expect(frames[1].offset, 55);
|
|
expect(frames[1].payloadLength, 5);
|
|
expect(frames[1].bytes.length, 14);
|
|
expect(frames[1].bytes.sublist(1, 5), [55, 0, 0, 0]);
|
|
expect(frames[1].bytes.sublist(9), image.sublist(55));
|
|
});
|
|
|
|
test('uses caller supplied payload size for low-MTU links', () {
|
|
final image = List<int>.generate(15, (index) => index);
|
|
final frames = BootloaderDfuProtocol.buildDataFrames(
|
|
imageBytes: image,
|
|
sessionId: 0x01,
|
|
payloadSize: 4,
|
|
);
|
|
|
|
expect(frames.map((frame) => frame.payloadLength), [4, 4, 4, 3]);
|
|
expect(frames.map((frame) => frame.offset), [0, 4, 8, 12]);
|
|
});
|
|
|
|
test('calculates safe payload size from negotiated MTU', () {
|
|
expect(
|
|
BootloaderDfuProtocol.maxPayloadSizeForMtu(64),
|
|
universalShifterBootloaderDfuMaxPayloadSizeBytes - 3,
|
|
);
|
|
expect(BootloaderDfuProtocol.maxPayloadSizeForMtu(23), 11);
|
|
expect(BootloaderDfuProtocol.maxPayloadSizeForMtu(12), 0);
|
|
expect(
|
|
BootloaderDfuProtocol.maxPayloadSizeForMtu(128),
|
|
universalShifterBootloaderDfuMaxPayloadSizeBytes,
|
|
);
|
|
});
|
|
});
|
|
|
|
group('BootloaderDfuProtocol status parsing', () {
|
|
test('parses bootloader status payload', () {
|
|
final status = BootloaderDfuProtocol.parseStatusPayload(
|
|
[0x00, 0x22, 0x78, 0x56, 0x34, 0x12],
|
|
);
|
|
|
|
expect(status.code, DfuBootloaderStatusCode.ok);
|
|
expect(status.rawCode, 0x00);
|
|
expect(status.sessionId, 0x22);
|
|
expect(status.expectedOffset, 0x12345678);
|
|
expect(status.isOk, isTrue);
|
|
});
|
|
|
|
test('preserves unknown status codes', () {
|
|
final status = BootloaderDfuProtocol.parseStatusPayload(
|
|
[0xFE, 0x00, 0x00, 0x00, 0x00, 0x00],
|
|
);
|
|
|
|
expect(status.code, DfuBootloaderStatusCode.unknown);
|
|
expect(status.rawCode, 0xFE);
|
|
});
|
|
|
|
test('rejects malformed status payloads', () {
|
|
expect(
|
|
() => BootloaderDfuProtocol.parseStatusPayload(const [0, 1]),
|
|
throwsFormatException,
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
List<int> _leU32Bytes(int value) {
|
|
return [
|
|
value & 0xFF,
|
|
(value >> 8) & 0xFF,
|
|
(value >> 16) & 0xFF,
|
|
(value >> 24) & 0xFF,
|
|
];
|
|
}
|