Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions app/src/test/java/org/nitri/opentopo/DistanceCalculatorTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.nitri.opentopo

import io.ticofab.androidgpxparser.parser.domain.Point
import org.junit.Assert.assertEquals
import org.junit.Test
import org.nitri.opentopo.util.DistanceCalculator
Expand Down Expand Up @@ -30,4 +31,55 @@ class DistanceCalculatorTest {

assertEquals(expectedDistanceMeters, distance, toleranceMeters)
}

@Test
fun distanceBetweenPointsHandlesAntipodalLocations() {
val pointNearGreenwich = createPoint(0.0, 0.0)
val antipodalPoint = createPoint(0.0, 180.0)

val distance = DistanceCalculator.distance(pointNearGreenwich, antipodalPoint)

val expectedDistanceMeters = Math.PI * 6_371_000.0
val toleranceMeters = 10_000.0

assertEquals(expectedDistanceMeters, distance, toleranceMeters)
}

private fun createPoint(latitude: Double, longitude: Double): Point {
val constructor = Point::class.java.declaredConstructors.firstOrNull { ctor ->
val parameterTypes = ctor.parameterTypes
parameterTypes.size >= 2 &&
parameterTypes[0].isLatitudeLongitudeParameter() &&
parameterTypes[1].isLatitudeLongitudeParameter()
} ?: error("Unable to locate Point constructor with latitude/longitude parameters")

constructor.isAccessible = true

val parameterTypes = constructor.parameterTypes
val arguments = Array<Any?>(parameterTypes.size) { index ->
when (index) {
0 -> latitude
1 -> longitude
else -> parameterTypes[index].defaultValue()
}
}

return constructor.newInstance(*arguments) as Point
}

private fun Class<*>.isLatitudeLongitudeParameter(): Boolean {
return this == java.lang.Double.TYPE || this == java.lang.Double::class.java
}

private fun Class<*>.defaultValue(): Any? = when (this) {
java.lang.Double.TYPE -> 0.0
java.lang.Float.TYPE -> 0f
java.lang.Long.TYPE -> 0L
java.lang.Integer.TYPE -> 0
java.lang.Boolean.TYPE -> false
java.lang.Short.TYPE -> 0.toShort()
java.lang.Byte.TYPE -> 0.toByte()
java.lang.Character.TYPE -> 0.toChar()
else -> null

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid constructing Point with nulls for non-null parameters

The reflective createPoint helper fills every constructor parameter after latitude/longitude with defaultValue() that returns null for reference types. io.ticofab.androidgpxparser.parser.domain.Point’s primary constructor includes non‑null parameters such as the extensions: List<Extension> argument which has a default value but is not nullable. Passing null through reflection triggers Kotlin’s parameter null checks and throws an IllegalArgumentException before the test reaches DistanceCalculator.distance, so the new test will reliably crash once the dependency is available. Construct Point using the public constructor with default arguments (e.g. Point(latitude, longitude) in Kotlin) or supply non‑null defaults for required parameters instead of null.

Useful? React with 👍 / 👎.

}
}
Loading