summaryrefslogtreecommitdiff
path: root/android-app/app/src/test/kotlin/com/example/androidapp
diff options
context:
space:
mode:
Diffstat (limited to 'android-app/app/src/test/kotlin/com/example/androidapp')
-rw-r--r--android-app/app/src/test/kotlin/com/example/androidapp/logbook/LogbookFormatterTest.kt178
1 files changed, 178 insertions, 0 deletions
diff --git a/android-app/app/src/test/kotlin/com/example/androidapp/logbook/LogbookFormatterTest.kt b/android-app/app/src/test/kotlin/com/example/androidapp/logbook/LogbookFormatterTest.kt
new file mode 100644
index 0000000..30b421f
--- /dev/null
+++ b/android-app/app/src/test/kotlin/com/example/androidapp/logbook/LogbookFormatterTest.kt
@@ -0,0 +1,178 @@
+package com.example.androidapp.logbook
+
+import com.example.androidapp.data.model.LogbookEntry
+import org.junit.Assert.*
+import org.junit.Test
+
+class LogbookFormatterTest {
+
+ // 2021-06-15 08:00:00 UTC = 1623744000000 ms
+ private val t0 = 1_623_744_000_000L
+
+ private fun entry(
+ ts: Long = t0,
+ lat: Double = 41.39,
+ lon: Double = -71.202,
+ sog: Double = 6.2,
+ cog: Double = 225.0,
+ windKt: Double? = 15.0,
+ windDir: Double? = 225.0,
+ baro: Double? = 1018.0,
+ depth: Double? = 14.0,
+ event: String? = "Departed slip",
+ notes: String? = null
+ ) = LogbookEntry(ts, lat, lon, sog, cog, windKt, windDir, baro, depth, event, notes)
+
+ // --- formatTime ---
+
+ @Test
+ fun `formatTime returns HH_MM for UTC midnight`() {
+ // 2021-06-15 00:00:00 UTC
+ val ts = 1_623_715_200_000L
+ assertEquals("00:00", LogbookFormatter.formatTime(ts))
+ }
+
+ @Test
+ fun `formatTime returns correct UTC hour for known timestamp`() {
+ // t0 = 2021-06-15 08:00:00 UTC
+ assertEquals("08:00", LogbookFormatter.formatTime(t0))
+ }
+
+ @Test
+ fun `formatTime pads single-digit hour and minute`() {
+ // 2021-06-15 01:05:00 UTC = 1623715200000 + 65*60*1000 = 1623715200000 + 3900000
+ val ts = 1_623_715_200_000L + 65 * 60_000L
+ assertEquals("01:05", LogbookFormatter.formatTime(ts))
+ }
+
+ // --- formatPosition ---
+
+ @Test
+ fun `formatPosition north east`() {
+ // 41.39°N → 41°23.4N, 71.202°E → 71°12.1E
+ val result = LogbookFormatter.formatPosition(41.39, 71.202)
+ assertEquals("41°23.4N 71°12.1E", result)
+ }
+
+ @Test
+ fun `formatPosition south west`() {
+ // -41.39°S → 41°23.4S, -71.202°W → 71°12.1W
+ val result = LogbookFormatter.formatPosition(-41.39, -71.202)
+ assertEquals("41°23.4S 71°12.1W", result)
+ }
+
+ @Test
+ fun `formatPosition zero zero`() {
+ val result = LogbookFormatter.formatPosition(0.0, 0.0)
+ assertEquals("0°0.0N 0°0.0E", result)
+ }
+
+ // --- formatWind ---
+
+ @Test
+ fun `formatWind null knots returns empty string`() {
+ assertEquals("", LogbookFormatter.formatWind(null, null))
+ }
+
+ @Test
+ fun `formatWind with knots and null direction returns knots only`() {
+ assertEquals("15kt", LogbookFormatter.formatWind(15.0, null))
+ }
+
+ @Test
+ fun `formatWind 225 degrees is SW`() {
+ assertEquals("15kt SW", LogbookFormatter.formatWind(15.0, 225.0))
+ }
+
+ @Test
+ fun `formatWind 0 degrees is N`() {
+ assertEquals("10kt N", LogbookFormatter.formatWind(10.0, 0.0))
+ }
+
+ @Test
+ fun `formatWind 360 degrees is N`() {
+ assertEquals("10kt N", LogbookFormatter.formatWind(10.0, 360.0))
+ }
+
+ @Test
+ fun `formatWind 90 degrees is E`() {
+ assertEquals("8kt E", LogbookFormatter.formatWind(8.0, 90.0))
+ }
+
+ // --- toCompassPoint ---
+
+ @Test
+ fun `toCompassPoint covers all 16 cardinal and intercardinal points`() {
+ val expected = listOf("N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
+ "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW")
+ expected.forEachIndexed { i, dir ->
+ val degrees = i * 22.5
+ assertEquals("degrees=$degrees", dir, LogbookFormatter.toCompassPoint(degrees))
+ }
+ }
+
+ // --- toRow ---
+
+ @Test
+ fun `toRow formats all fields correctly`() {
+ val row = LogbookFormatter.toRow(entry())
+ assertEquals("08:00", row.time)
+ assertEquals("41°23.4N 71°12.1W", row.position)
+ assertEquals("6.2", row.sog)
+ assertEquals("225", row.cog)
+ assertEquals("15kt SW", row.wind)
+ assertEquals("1018", row.baro)
+ assertEquals("14m", row.depth)
+ assertEquals("Departed slip", row.eventNotes)
+ }
+
+ @Test
+ fun `toRow combines event and notes with colon`() {
+ val row = LogbookFormatter.toRow(entry(event = "Reef #1", notes = "Strong gusts"))
+ assertEquals("Reef #1: Strong gusts", row.eventNotes)
+ }
+
+ @Test
+ fun `toRow with only notes has no colon prefix`() {
+ val row = LogbookFormatter.toRow(entry(event = null, notes = "Calm seas"))
+ assertEquals("Calm seas", row.eventNotes)
+ }
+
+ @Test
+ fun `toRow with null optional fields uses empty strings`() {
+ val e = LogbookEntry(t0, 0.0, 0.0, 0.0, 0.0)
+ val row = LogbookFormatter.toRow(e)
+ assertEquals("", row.wind)
+ assertEquals("", row.baro)
+ assertEquals("", row.depth)
+ assertEquals("", row.eventNotes)
+ }
+
+ // --- toPage ---
+
+ @Test
+ fun `toPage returns page with default title and correct column count`() {
+ val page = LogbookFormatter.toPage(emptyList())
+ assertEquals("Trip Logbook", page.title)
+ assertEquals(8, page.columns.size)
+ }
+
+ @Test
+ fun `toPage maps entries to rows in order`() {
+ val entries = listOf(
+ entry(ts = t0, event = "First"),
+ entry(ts = t0 + 3_600_000L, event = "Second")
+ )
+ val page = LogbookFormatter.toPage(entries, "Voyage Log")
+ assertEquals("Voyage Log", page.title)
+ assertEquals(2, page.rows.size)
+ assertEquals("First", page.rows[0].eventNotes)
+ assertEquals("Second", page.rows[1].eventNotes)
+ }
+
+ @Test
+ fun `toPage empty entries produces empty rows`() {
+ val page = LogbookFormatter.toPage(emptyList())
+ assertTrue(page.rows.isEmpty())
+ }
+}