Files
dmc/app/test/domain/mission_stats_test.dart
Constantin Leue 6b7977eca1 Show distance to home as a fourth field in the fly-mode details pill
Adds computeDistanceToHomeM() (mission_stats.dart) alongside the
existing rubber-band-target helper, wired through MissionFooterBar ->
BottomStatsBar the same way flyMissionStatus already is. Only appears
once a home point is known, as the last field after waypoint/distance/
duration.

Generalized HomePointIcon to take an optional strokeColor: the map
marker keeps its dark-fill-plus-white-outline look, while the details
pill uses a plain white fill (strokeColor: null) to match the existing
monochrome flag/ruler/clock icons instead of looking like a dropped-in
map pin.
2026-08-09 21:49:03 +02:00

216 lines
6.7 KiB
Dart

import 'package:dmc_app/domain/mission/mission_stats.dart';
import 'package:dmc_app/domain/waypoint/flat_waypoint_list.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:latlong2/latlong.dart';
Waypoint _wp(double lat, double lon, {double speed = 15}) =>
Waypoint(lat: lat, lon: lon, altitudeM: 60, speedMs: speed, catchRadiusM: 60);
void main() {
group('computeMissionStats', () {
test('leere/einzelne Wegpunktliste ergibt Distanz/Dauer 0', () {
expect(computeMissionStats(const []).distanceM, 0);
expect(computeMissionStats([_wp(0, 0)]).distanceM, 0);
expect(computeMissionStats([_wp(0, 0)]).durationSec, 0);
});
test('zwei Wegpunkte: Distanz aus Grosskreis, Dauer aus Zielgeschwindigkeit', () {
final stats = computeMissionStats([_wp(0, 0), _wp(0, 0.01, speed: 20)]);
// 0.01 Grad Laengendifferenz am Aequator entspricht rund 1112 m.
expect(stats.distanceM, closeTo(1112.6, 1));
expect(stats.durationSec, closeTo(stats.distanceM / 20, 0.01));
});
test('mehrere Beine summieren sich, Dauer je Bein mit dessen Zielgeschwindigkeit', () {
final stats = computeMissionStats(
[_wp(0, 0, speed: 10), _wp(0, 0.01, speed: 20), _wp(0, 0.02, speed: 10)],
);
final leg1 = 1112.6;
final leg2 = 1112.6;
expect(stats.distanceM, closeTo(leg1 + leg2, 2));
// Erstes Bein zaehlt mit der Geschwindigkeit des Zielwegpunkts (20),
// zweites Bein mit dessen Zielwegpunkt (10).
expect(stats.durationSec, closeTo(leg1 / 20 + leg2 / 10, 0.1));
});
});
group('computeFlyMissionStatus', () {
final waypoints = [_wp(0, 0), _wp(0, 0.01), _wp(0, 0.02)];
test('kein aktiver Wegpunkt (null) ergibt null', () {
expect(
computeFlyMissionStatus(waypoints: waypoints, activeWaypointIndex: null),
isNull,
);
});
test('activeWaypointIndex ausserhalb der Liste ergibt null', () {
expect(
computeFlyMissionStatus(waypoints: waypoints, activeWaypointIndex: 3),
isNull,
);
expect(
computeFlyMissionStatus(waypoints: waypoints, activeWaypointIndex: -1),
isNull,
);
});
test('activeWaypointNumber ist 1-basiert (Index 1 -> Nummer 2)', () {
final status = computeFlyMissionStatus(
waypoints: waypoints,
activeWaypointIndex: 1,
);
expect(status!.activeWaypointNumber, 2);
expect(status.waypointCount, 3);
});
test('ohne Position (kein Fix) bleiben Distanz/ETA null', () {
final status = computeFlyMissionStatus(
waypoints: waypoints,
activeWaypointIndex: 1,
);
expect(status!.distanceToActiveWaypointM, isNull);
expect(status.etaToActiveWaypointSec, isNull);
});
test('mit Position wird die Distanz zum aktiven Wegpunkt berechnet', () {
final status = computeFlyMissionStatus(
waypoints: waypoints,
activeWaypointIndex: 1,
currentLat: 0,
currentLon: 0,
);
// Wegpunkt 1 (Index 1) liegt bei (0, 0.01) - rund 1112.6 m vom
// Ursprung entfernt.
expect(status!.distanceToActiveWaypointM, closeTo(1112.6, 1));
// Ohne Geschwindigkeit keine ETA.
expect(status.etaToActiveWaypointSec, isNull);
});
test('mit Position und Geschwindigkeit wird die ETA berechnet', () {
final status = computeFlyMissionStatus(
waypoints: waypoints,
activeWaypointIndex: 1,
currentLat: 0,
currentLon: 0,
currentSpeedMs: 10,
);
expect(status!.etaToActiveWaypointSec, closeTo(1112.6 / 10, 0.1));
});
test('unter 0.5 m/s wird keine (irrefuehrende) ETA berechnet', () {
final status = computeFlyMissionStatus(
waypoints: waypoints,
activeWaypointIndex: 1,
currentLat: 0,
currentLon: 0,
currentSpeedMs: 0.1,
);
expect(status!.etaToActiveWaypointSec, isNull);
});
});
group('computeRubberBandTarget', () {
final waypoints = [_wp(0, 0), _wp(0, 0.01), _wp(0, 0.02)];
const home = LatLng(1, 1);
test('zeigt normalerweise auf den aktiven Missionswegpunkt', () {
final target = computeRubberBandTarget(
navMode: 3, // Waypoint mission
activeWaypointIndex: 1,
waypoints: waypoints,
homePoint: home,
);
expect(target, const LatLng(0, 0.01));
});
test('zeigt bei Return-to-Home (navMode 2) auf den Homepoint statt auf '
'den (ggf. laengst passierten) eingefrorenen Missionswegpunkt', () {
final target = computeRubberBandTarget(
navMode: navModeReturnToHome,
activeWaypointIndex: 1,
waypoints: waypoints,
homePoint: home,
);
expect(target, home);
});
test('liefert null bei RTH ohne bekannten Homepoint statt eines '
'erfundenen Ziels', () {
final target = computeRubberBandTarget(
navMode: navModeReturnToHome,
activeWaypointIndex: 1,
waypoints: waypoints,
homePoint: null,
);
expect(target, isNull);
});
test('schaltet nach Abbruch von RTH (navMode wieder 3) automatisch '
'zurueck auf den Missionswegpunkt', () {
const rth = 2;
final duringRth = computeRubberBandTarget(
navMode: rth,
activeWaypointIndex: 1,
waypoints: waypoints,
homePoint: home,
);
final afterAbort = computeRubberBandTarget(
navMode: 3,
activeWaypointIndex: 1,
waypoints: waypoints,
homePoint: home,
);
expect(duringRth, home);
expect(afterAbort, const LatLng(0, 0.01));
});
test('activeWaypointIndex null oder ausserhalb der Liste ergibt null '
'(ausserhalb RTH)', () {
expect(
computeRubberBandTarget(
navMode: 3,
activeWaypointIndex: null,
waypoints: waypoints,
),
isNull,
);
expect(
computeRubberBandTarget(
navMode: 3,
activeWaypointIndex: 3,
waypoints: waypoints,
),
isNull,
);
});
});
group('computeDistanceToHomeM', () {
test('berechnet die Distanz aus Grosskreis', () {
final distance = computeDistanceToHomeM(
currentPosition: const LatLng(0, 0),
homePoint: const LatLng(0, 0.01),
);
// 0.01 Grad Laengendifferenz am Aequator entspricht rund 1112 m.
expect(distance, closeTo(1112.6, 1));
});
test('null ohne aktuelle Position (kein GPS-Fix)', () {
expect(
computeDistanceToHomeM(currentPosition: null, homePoint: const LatLng(0, 0)),
isNull,
);
});
test('null ohne bekannten Homepoint', () {
expect(
computeDistanceToHomeM(currentPosition: const LatLng(0, 0), homePoint: null),
isNull,
);
});
});
}