WIFI connection support added

This commit is contained in:
Constantin Leue
2026-07-31 08:17:12 +02:00
parent b28b1a89bb
commit 8cc9a51a34
11 changed files with 1436 additions and 115 deletions
+144
View File
@@ -0,0 +1,144 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:dmc_app/transport/link_transport.dart';
import 'package:dmc_app/transport/udp/udp_transport.dart';
void main() {
// Port 0 laesst das Betriebssystem einen freien Port waehlen, damit die
// Tests nicht mit einer echten mLRS-Bruecke oder parallelen Testlaeufen
// um Port 14550 konkurrieren.
Future<UdpTransport> openTransport({String? host}) async {
final transport = UdpTransport(
getHost: () async => host,
getPort: () async => 0,
);
await transport.connect();
return transport;
}
test('connect() bindet einen lokalen Port und meldet connected', () async {
final transport = await openTransport();
final states = <LinkState>[];
transport.state.listen(states.add);
expect(transport.currentState, LinkState.connected);
expect(transport.boundPort, isNotNull);
expect(transport.boundPort, greaterThan(0));
transport.dispose();
});
test('eingehende Pakete kommen ueber incoming an und lernen die Gegenstelle',
() async {
final transport = await openTransport();
final sender = await RawDatagramSocket.bind(InternetAddress.loopbackIPv4, 0);
final received = <Uint8List>[];
transport.incoming.listen(received.add);
sender.send(
Uint8List.fromList([1, 2, 3]),
InternetAddress.loopbackIPv4,
transport.boundPort!,
);
await Future<void>.delayed(const Duration(milliseconds: 50));
expect(received, [
Uint8List.fromList([1, 2, 3]),
]);
expect(transport.peerDescription, contains(sender.port.toString()));
sender.close();
transport.dispose();
});
test('send() ohne bekannte Gegenstelle wirft LinkConnectException', () async {
final transport = await openTransport();
expect(
() => transport.send(Uint8List.fromList([1])),
throwsA(isA<LinkConnectException>()),
);
transport.dispose();
});
test('send() schickt Bytes an die gelernte Gegenstelle', () async {
final transport = await openTransport();
final peer = await RawDatagramSocket.bind(InternetAddress.loopbackIPv4, 0);
// Erst "sprechen" lassen, damit der Transport die Gegenstelle lernt
// (Doku Kommunikationsschicht Abschnitt 3: "Gegenstelle aus dem Verkehr
// lernen").
peer.send(
Uint8List.fromList([9]),
InternetAddress.loopbackIPv4,
transport.boundPort!,
);
await Future<void>.delayed(const Duration(milliseconds: 50));
final receivedByPeer = <Uint8List>[];
peer.listen((event) {
if (event == RawSocketEvent.read) {
final datagram = peer.receive();
if (datagram != null) receivedByPeer.add(datagram.data);
}
});
await transport.send(Uint8List.fromList([7, 7, 7]));
await Future<void>.delayed(const Duration(milliseconds: 50));
expect(receivedByPeer, [
Uint8List.fromList([7, 7, 7]),
]);
peer.close();
transport.dispose();
});
test('disconnect() setzt LinkState.disconnected', () async {
final transport = await openTransport();
await transport.disconnect();
expect(transport.currentState, LinkState.disconnected);
expect(transport.boundPort, isNull);
transport.dispose();
});
test('weicht auf einen fluechtigen Port aus, wenn der konfigurierte belegt ist',
() async {
final occupied = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0);
final transport = UdpTransport(
getHost: () async => null,
getPort: () async => occupied.port,
);
await transport.connect();
expect(transport.currentState, LinkState.connected);
expect(transport.boundPort, isNot(occupied.port));
expect(transport.lastErrorMessage, contains('belegt'));
occupied.close();
transport.dispose();
});
test('konfigurierte Host-Vorgabe wird als initiale Gegenstelle uebernommen',
() async {
final transport = UdpTransport(
getHost: () async => '127.0.0.1',
getPort: () async => 0,
);
await transport.connect();
expect(transport.peerDescription, isNotNull);
expect(transport.peerDescription, startsWith('127.0.0.1'));
transport.dispose();
});
}