import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:dmc_app/transport/msp/msp_frame.dart'; import 'package:dmc_app/transport/msp/msp_frame_decoder.dart'; /// Fuettert [bytes] in Stuecken der Groesse [chunkSize] und sammelt alle /// dekodierten Rahmen ueber den gesamten Strom. List _decodeInChunks(Uint8List bytes, int chunkSize) { final decoder = MspFrameDecoder(); final frames = []; for (var offset = 0; offset < bytes.length; offset += chunkSize) { final end = (offset + chunkSize).clamp(0, bytes.length); frames.addAll(decoder.addBytes(bytes.sublist(offset, end))); } return frames; } void main() { final altitudePayload = Uint8List.fromList( [0xD2, 0x04, 0x00, 0x00, 0xCE, 0xFF, 0xB0, 0x04, 0x00, 0x00], ); final altitudeFrameBytes = encodeMspV2Frame(MspDirection.response, 109, altitudePayload); // Abnahmekriterium (Doku Kommunikationsschicht 8): dieselbe Bytefolge in // Stuecken von 1, 7 und 512 Byte muss dasselbe Ergebnis liefern. for (final chunkSize in [1, 7, 512]) { test('dekodiert einen MSP_ALTITUDE-Rahmen bei Chunk-Groesse $chunkSize', () { final frames = _decodeInChunks(altitudeFrameBytes, chunkSize); expect(frames, hasLength(1)); expect(frames.single.direction, MspDirection.response); expect(frames.single.function, 109); expect(frames.single.payload, altitudePayload); }); } test('mehrere Rahmen hintereinander in einem Chunk werden alle erkannt', () { final gpsFrameBytes = encodeMspV2Frame(MspDirection.response, 106, Uint8List.fromList([1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])); final combined = Uint8List.fromList([ ...altitudeFrameBytes, ...gpsFrameBytes, ]); final frames = _decodeInChunks(combined, 512); expect(frames, hasLength(2)); expect(frames[0].function, 109); expect(frames[1].function, 106); }); test('fehlerhafte CRC wird verworfen, ohne den Strom zu verlieren', () { final corrupted = Uint8List.fromList(altitudeFrameBytes); corrupted[corrupted.length - 1] ^= 0xFF; // letztes Byte (CRC) kippen final decoder = MspFrameDecoder(); final framesFromCorrupted = decoder.addBytes(corrupted); expect(framesFromCorrupted, isEmpty); // Direkt danach ein gueltiger Rahmen auf demselben Decoder - der // Automat muss wieder im Idle-Zustand sein und normal weiterlesen. final framesFromValid = decoder.addBytes(altitudeFrameBytes); expect(framesFromValid, hasLength(1)); expect(framesFromValid.single.function, 109); }); test('Fehlerantwort (dir = \'!\') wird als solche erkannt', () { final errorFrameBytes = encodeMspV2Frame(MspDirection.error, 109, Uint8List(0)); final frames = MspFrameDecoder().addBytes(errorFrameBytes); expect(frames, hasLength(1)); expect(frames.single.direction, MspDirection.error); expect(frames.single.function, 109); }); test('Muell vor dem eigentlichen Rahmen wird ignoriert', () { final garbage = Uint8List.fromList([0x00, 0x24, 0x11, 0x58, 0xFF]); final combined = Uint8List.fromList([...garbage, ...altitudeFrameBytes]); final frames = _decodeInChunks(combined, 7); expect(frames, hasLength(1)); expect(frames.single.function, 109); }); }