diff options
| author | Claudomator Agent <agent@claudomator> | 2026-03-14 01:47:20 +0000 |
|---|---|---|
| committer | Claudomator Agent <agent@claudomator> | 2026-03-14 01:47:20 +0000 |
| commit | 010d25c3e7e37ba109117a93e4d1c0f8802b01a9 (patch) | |
| tree | 13240873666277df79eab89d71643ee9f00d7158 /android-app/app/src/test/kotlin/org | |
| parent | 3f18f770e9d33c5e5d0657c6160fa8f30b21831f (diff) | |
Add GpsProvider interface and DeviceGpsProvider (FusedLocation)
- GpsPosition: lat/lon/sog (knots)/cog (degrees true)/timestampMs
- GpsProvider + GpsListener interfaces for provider abstraction
- DeviceGpsProvider wraps LocationManager GPS_PROVIDER (1 Hz default)
SOG: m/s × 1.94384 knots; fix-lost timeout 10s
- FakeGpsProvider + 9 passing JVM unit tests (no Android deps)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'android-app/app/src/test/kotlin/org')
| -rw-r--r-- | android-app/app/src/test/kotlin/org/terst/nav/gps/GpsProviderTest.kt | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/android-app/app/src/test/kotlin/org/terst/nav/gps/GpsProviderTest.kt b/android-app/app/src/test/kotlin/org/terst/nav/gps/GpsProviderTest.kt new file mode 100644 index 0000000..4a03387 --- /dev/null +++ b/android-app/app/src/test/kotlin/org/terst/nav/gps/GpsProviderTest.kt @@ -0,0 +1,133 @@ +package org.terst.nav.gps + +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test + +// ── Fake implementation (no Android dependencies) ──────────────────────────── + +class FakeGpsProvider : GpsProvider { + var currentPosition: GpsPosition? = null + private val listeners = mutableListOf<GpsListener>() + var started = false + + override fun start() { started = true } + override fun stop() { started = false } + override val position: GpsPosition? get() = currentPosition + override fun addListener(listener: GpsListener) { listeners.add(listener) } + override fun removeListener(listener: GpsListener) { listeners.remove(listener) } + + fun simulatePosition(pos: GpsPosition) { + currentPosition = pos + listeners.forEach { it.onPositionUpdate(pos) } + } + + fun simulateFixLost() { listeners.forEach { it.onFixLost() } } +} + +// ── Test helpers ───────────────────────────────────────────────────────────── + +private fun makePosition(lat: Double = 41.0, lon: Double = -71.0, sog: Double = 5.0) = + GpsPosition(lat, lon, sog, cog = 180.0, timestampMs = 1_000L) + +private class RecordingListener : GpsListener { + val positions = mutableListOf<GpsPosition>() + var fixLostCount = 0 + + override fun onPositionUpdate(position: GpsPosition) { positions.add(position) } + override fun onFixLost() { fixLostCount++ } +} + +// ── Tests ───────────────────────────────────────────────────────────────────── + +class GpsProviderTest { + + private lateinit var provider: FakeGpsProvider + + @Before + fun setUp() { + provider = FakeGpsProvider() + } + + @Test + fun `start sets started to true`() { + provider.start() + assertTrue(provider.started) + } + + @Test + fun `stop sets started to false`() { + provider.start() + provider.stop() + assertFalse(provider.started) + } + + @Test + fun `listener receives position update`() { + val listener = RecordingListener() + provider.addListener(listener) + val pos = makePosition() + provider.simulatePosition(pos) + assertEquals(1, listener.positions.size) + assertEquals(pos, listener.positions[0]) + } + + @Test + fun `listener notified of fix lost`() { + val listener = RecordingListener() + provider.addListener(listener) + provider.simulateFixLost() + assertEquals(1, listener.fixLostCount) + } + + @Test + fun `multiple listeners all receive position update`() { + val l1 = RecordingListener() + val l2 = RecordingListener() + val l3 = RecordingListener() + provider.addListener(l1) + provider.addListener(l2) + provider.addListener(l3) + provider.simulatePosition(makePosition()) + assertEquals(1, l1.positions.size) + assertEquals(1, l2.positions.size) + assertEquals(1, l3.positions.size) + } + + @Test + fun `multiple listeners all notified of fix lost`() { + val l1 = RecordingListener() + val l2 = RecordingListener() + provider.addListener(l1) + provider.addListener(l2) + provider.simulateFixLost() + assertEquals(1, l1.fixLostCount) + assertEquals(1, l2.fixLostCount) + } + + @Test + fun `removing listener stops notifications`() { + val listener = RecordingListener() + provider.addListener(listener) + provider.removeListener(listener) + provider.simulatePosition(makePosition()) + provider.simulateFixLost() + assertEquals(0, listener.positions.size) + assertEquals(0, listener.fixLostCount) + } + + @Test + fun `position property reflects last simulated position`() { + assertNull(provider.position) + val pos = makePosition(lat = 42.5, lon = -70.0) + provider.simulatePosition(pos) + assertEquals(pos, provider.position) + } + + @Test + fun `SOG conversion sanity check - 1 mps is approximately 1_94384 knots`() { + // 1 m/s * 1.94384 = 1.94384 knots — validate constant used in DeviceGpsProvider + val knots = 1.0 * 1.94384 + assertEquals(1.94384, knots, 0.00001) + } +} |
