summaryrefslogtreecommitdiff
path: root/android-app/app/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'android-app/app/src/test')
-rw-r--r--android-app/app/src/test/kotlin/com/example/androidapp/gps/LocationServiceTest.kt110
1 files changed, 110 insertions, 0 deletions
diff --git a/android-app/app/src/test/kotlin/com/example/androidapp/gps/LocationServiceTest.kt b/android-app/app/src/test/kotlin/com/example/androidapp/gps/LocationServiceTest.kt
index d9192c6..237004b 100644
--- a/android-app/app/src/test/kotlin/com/example/androidapp/gps/LocationServiceTest.kt
+++ b/android-app/app/src/test/kotlin/com/example/androidapp/gps/LocationServiceTest.kt
@@ -114,4 +114,114 @@ class LocationServiceTest {
assertEquals(data, svc.latestSensor.first())
}
+
+ // ── GPS sensor fusion ─────────────────────────────────────────────────────
+
+ private fun fusionService(
+ nmeaStalenessThresholdMs: Long = 5_000L,
+ clockMs: () -> Long = System::currentTimeMillis
+ ) = LocationService(
+ nmeaStalenessThresholdMs = nmeaStalenessThresholdMs,
+ clockMs = clockMs
+ )
+
+ private fun pos(lat: Double, lon: Double, timestampMs: Long) =
+ GpsPosition(lat, lon, sog = 0.0, cog = 0.0, timestampMs = timestampMs)
+
+ @Test
+ fun noGpsData_bestPositionNullAndSourceNone() = runBlocking {
+ val svc = fusionService()
+ assertNull(svc.bestPosition.first())
+ assertEquals(GpsSource.NONE, svc.activeGpsSource.first())
+ }
+
+ @Test
+ fun freshNmea_preferredOverAndroid() = runBlocking {
+ val now = 10_000L
+ val svc = fusionService(nmeaStalenessThresholdMs = 5_000L, clockMs = { now })
+
+ val nmeaFix = pos(41.0, -71.0, now)
+ val androidFix = pos(42.0, -72.0, now - 1_000L)
+
+ svc.updateAndroidGps(androidFix)
+ svc.updateNmeaGps(nmeaFix)
+
+ assertEquals(GpsSource.NMEA, svc.activeGpsSource.first())
+ assertEquals(nmeaFix, svc.bestPosition.first())
+ }
+
+ @Test
+ fun staleNmea_androidFallback() = runBlocking {
+ val nmeaTime = 0L
+ val now = 10_000L // 10 s later — NMEA is stale (threshold 5 s)
+ val svc = fusionService(nmeaStalenessThresholdMs = 5_000L, clockMs = { now })
+
+ val nmeaFix = pos(41.0, -71.0, nmeaTime)
+ val androidFix = pos(42.0, -72.0, now)
+
+ svc.updateNmeaGps(nmeaFix)
+ svc.updateAndroidGps(androidFix)
+
+ assertEquals(GpsSource.ANDROID, svc.activeGpsSource.first())
+ assertEquals(androidFix, svc.bestPosition.first())
+ }
+
+ @Test
+ fun onlyNmeaAvailable_usedEvenWhenStale() = runBlocking {
+ val now = 60_000L // 60 s after fix — very stale
+ val svc = fusionService(nmeaStalenessThresholdMs = 5_000L, clockMs = { now })
+
+ val nmeaFix = pos(41.0, -71.0, 0L)
+ svc.updateNmeaGps(nmeaFix)
+
+ assertEquals(GpsSource.NMEA, svc.activeGpsSource.first())
+ assertEquals(nmeaFix, svc.bestPosition.first())
+ }
+
+ @Test
+ fun onlyAndroidAvailable_isUsed() = runBlocking {
+ val svc = fusionService()
+ val androidFix = pos(42.0, -72.0, System.currentTimeMillis())
+ svc.updateAndroidGps(androidFix)
+
+ assertEquals(GpsSource.ANDROID, svc.activeGpsSource.first())
+ assertEquals(androidFix, svc.bestPosition.first())
+ }
+
+ @Test
+ fun nmeaAtExactThreshold_isConsideredFresh() = runBlocking {
+ val fixTime = 0L
+ val now = 5_000L // exactly at threshold
+ val svc = fusionService(nmeaStalenessThresholdMs = 5_000L, clockMs = { now })
+
+ val nmeaFix = pos(41.0, -71.0, fixTime)
+ val androidFix = pos(42.0, -72.0, now)
+
+ svc.updateNmeaGps(nmeaFix)
+ svc.updateAndroidGps(androidFix)
+
+ assertEquals(GpsSource.NMEA, svc.activeGpsSource.first())
+ }
+
+ @Test
+ fun nmeaRecovery_switchesBackFromAndroid() = runBlocking {
+ var now = 0L
+ val svc = fusionService(nmeaStalenessThresholdMs = 5_000L, clockMs = { now })
+
+ // Fresh NMEA
+ svc.updateNmeaGps(pos(41.0, -71.0, 0L))
+ assertEquals(GpsSource.NMEA, svc.activeGpsSource.value)
+
+ // NMEA goes stale; Android takes over
+ now = 10_000L
+ val androidFix = pos(42.0, -72.0, 10_000L)
+ svc.updateAndroidGps(androidFix)
+ assertEquals(GpsSource.ANDROID, svc.activeGpsSource.value)
+
+ // NMEA recovers with a fresh fix
+ val freshNmea = pos(41.1, -71.1, 10_000L)
+ svc.updateNmeaGps(freshNmea)
+ assertEquals(GpsSource.NMEA, svc.activeGpsSource.value)
+ assertEquals(freshNmea, svc.bestPosition.value)
+ }
}