udp socket binding istead of process binding to allow internet over network for maps etc
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@@ -9,24 +8,34 @@ import 'package:dmc_app/transport/udp/mlrs_network_controller.dart';
|
||||
import 'package:dmc_app/transport/udp/udp_transport.dart';
|
||||
|
||||
/// Test-Doppel fuer [MlrsNetworkController] (Doku Kommunikationsschicht v2,
|
||||
/// Abschnitt 4): simuliert Netzanfrage-Ergebnisse ueber [emit], ohne einen
|
||||
/// echten Android-Platform-Channel zu brauchen. Zeichnet Aufrufe auf, damit
|
||||
/// Tests den Bindungsfenster-Ablauf (bind -> Socket -> unbind) pruefen
|
||||
/// koennen.
|
||||
/// Abschnitt 4): simuliert Netzanfrage- und Socket-Ergebnisse rein in Dart,
|
||||
/// ohne einen echten Android-Platform-Channel/nativen Socket zu brauchen.
|
||||
/// Der eigentliche `DatagramSocket` lebt seit der Umstellung auf
|
||||
/// socket-genaue Bindung (statt Prozessbindung) nativ in
|
||||
/// MlrsNetworkPlugin.kt - dieses Fake deckt daher nur noch die
|
||||
/// Orchestrierung in UdpTransport ab, nicht mehr echtes UDP-Verhalten.
|
||||
class FakeMlrsNetworkController implements MlrsNetworkController {
|
||||
final _eventsController = StreamController<MlrsNetworkEvent>.broadcast();
|
||||
final _packetsController = StreamController<MlrsSocketPacket>.broadcast();
|
||||
|
||||
final List<String> requestedSsidPrefixes = [];
|
||||
final List<({Uint8List data, String host, int port})> sentPackets = [];
|
||||
int releaseCount = 0;
|
||||
int bindCount = 0;
|
||||
int unbindCount = 0;
|
||||
bool bindResult = true;
|
||||
int openSocketCount = 0;
|
||||
int closeSocketCount = 0;
|
||||
bool? staConcurrencyResult;
|
||||
|
||||
int nextBoundPort = 14550;
|
||||
int? nextFallbackFromPort;
|
||||
|
||||
@override
|
||||
Stream<MlrsNetworkEvent> get events => _eventsController.stream;
|
||||
|
||||
@override
|
||||
Stream<MlrsSocketPacket> get incomingPackets => _packetsController.stream;
|
||||
|
||||
void emit(MlrsNetworkEvent event) => _eventsController.add(event);
|
||||
void emitPacket(MlrsSocketPacket packet) => _packetsController.add(packet);
|
||||
|
||||
@override
|
||||
Future<void> requestNetwork(String ssidPrefix) async {
|
||||
@@ -39,14 +48,22 @@ class FakeMlrsNetworkController implements MlrsNetworkController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> bindProcessToNetwork() async {
|
||||
bindCount++;
|
||||
return bindResult;
|
||||
Future<UdpSocketBindResult> openSocket(int port) async {
|
||||
openSocketCount++;
|
||||
return UdpSocketBindResult(
|
||||
boundPort: nextBoundPort,
|
||||
fallbackFromPort: nextFallbackFromPort,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> unbindProcessFromNetwork() async {
|
||||
unbindCount++;
|
||||
Future<void> closeSocket() async {
|
||||
closeSocketCount++;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> send(Uint8List data, String host, int port) async {
|
||||
sentPackets.add((data: data, host: host, port: port));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -55,6 +72,7 @@ class FakeMlrsNetworkController implements MlrsNetworkController {
|
||||
@override
|
||||
void dispose() {
|
||||
_eventsController.close();
|
||||
_packetsController.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,16 +114,14 @@ void main() {
|
||||
transport.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'connect() binds process to network, creates the socket, then unbinds',
|
||||
test('connect() opens the native socket and reports the bound port',
|
||||
() async {
|
||||
final fake = FakeMlrsNetworkController();
|
||||
final fake = FakeMlrsNetworkController()..nextBoundPort = 14550;
|
||||
final transport = await connectWithFake(fake);
|
||||
|
||||
expect(transport.currentState, LinkState.connected);
|
||||
expect(transport.boundPort, isNotNull);
|
||||
expect(fake.bindCount, 1);
|
||||
expect(fake.unbindCount, 1);
|
||||
expect(transport.boundPort, 14550);
|
||||
expect(fake.openSocketCount, 1);
|
||||
|
||||
transport.dispose();
|
||||
});
|
||||
@@ -130,6 +146,7 @@ void main() {
|
||||
);
|
||||
expect(transport.lastErrorReason, LinkErrorReason.networkUnavailable);
|
||||
expect(transport.currentState, LinkState.error);
|
||||
expect(fake.openSocketCount, 0);
|
||||
|
||||
transport.dispose();
|
||||
});
|
||||
@@ -145,6 +162,7 @@ void main() {
|
||||
expect(transport.currentState, LinkState.error);
|
||||
expect(transport.lastErrorReason, LinkErrorReason.networkLost);
|
||||
expect(transport.boundPort, isNull);
|
||||
expect(fake.closeSocketCount, 1);
|
||||
|
||||
transport.dispose();
|
||||
});
|
||||
@@ -178,24 +196,22 @@ void main() {
|
||||
test('incoming packets arrive via incoming and learn the peer', () async {
|
||||
final fake = FakeMlrsNetworkController();
|
||||
final transport = await connectWithFake(fake);
|
||||
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));
|
||||
fake.emitPacket(MlrsSocketPacket(
|
||||
address: '192.168.4.100',
|
||||
port: 5555,
|
||||
data: Uint8List.fromList([1, 2, 3]),
|
||||
));
|
||||
await pump();
|
||||
|
||||
expect(received, [
|
||||
Uint8List.fromList([1, 2, 3]),
|
||||
]);
|
||||
expect(transport.peerDescription, contains(sender.port.toString()));
|
||||
expect(transport.peerDescription, '192.168.4.100:5555');
|
||||
|
||||
sender.close();
|
||||
transport.dispose();
|
||||
});
|
||||
|
||||
@@ -211,37 +227,25 @@ void main() {
|
||||
transport.dispose();
|
||||
});
|
||||
|
||||
test('send() sends bytes to the learned peer', () async {
|
||||
test('send() forwards bytes to the learned peer via the native socket',
|
||||
() async {
|
||||
final fake = FakeMlrsNetworkController();
|
||||
final transport = await connectWithFake(fake);
|
||||
final peer = await RawDatagramSocket.bind(InternetAddress.loopbackIPv4, 0);
|
||||
|
||||
// Erst "sprechen" lassen, damit der Transport die Gegenstelle lernt
|
||||
// (Doku Kommunikationsschicht v2 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);
|
||||
}
|
||||
});
|
||||
fake.emitPacket(MlrsSocketPacket(
|
||||
address: '192.168.4.100',
|
||||
port: 5555,
|
||||
data: Uint8List.fromList([9]),
|
||||
));
|
||||
await pump();
|
||||
|
||||
await transport.send(Uint8List.fromList([7, 7, 7]));
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(receivedByPeer, [
|
||||
Uint8List.fromList([7, 7, 7]),
|
||||
]);
|
||||
expect(fake.sentPackets, hasLength(1));
|
||||
expect(fake.sentPackets.single.data, Uint8List.fromList([7, 7, 7]));
|
||||
expect(fake.sentPackets.single.host, '192.168.4.100');
|
||||
expect(fake.sentPackets.single.port, 5555);
|
||||
|
||||
peer.close();
|
||||
transport.dispose();
|
||||
});
|
||||
|
||||
@@ -254,23 +258,23 @@ void main() {
|
||||
|
||||
expect(transport.currentState, LinkState.disconnected);
|
||||
expect(transport.boundPort, isNull);
|
||||
expect(fake.closeSocketCount, 1);
|
||||
expect(fake.releaseCount, greaterThanOrEqualTo(1));
|
||||
|
||||
transport.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'falls back to an ephemeral port when the configured one is occupied',
|
||||
test('reports a fallback port when the configured one was occupied natively',
|
||||
() async {
|
||||
final occupied = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0);
|
||||
final fake = FakeMlrsNetworkController();
|
||||
final transport = await connectWithFake(fake, port: occupied.port);
|
||||
final fake = FakeMlrsNetworkController()
|
||||
..nextBoundPort = 51000
|
||||
..nextFallbackFromPort = 14550;
|
||||
final transport = await connectWithFake(fake, port: 14550);
|
||||
|
||||
expect(transport.currentState, LinkState.connected);
|
||||
expect(transport.boundPort, isNot(occupied.port));
|
||||
expect(transport.boundPort, 51000);
|
||||
expect(transport.lastErrorMessage, contains('in use'));
|
||||
|
||||
occupied.close();
|
||||
transport.dispose();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user