From ab0204f3dc517f8367f6ebfcb0abf0f622b26718 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Thu, 23 Apr 2026 15:14:05 -0500 Subject: [PATCH] test: add StreamData properties for Aprsme.GeoUtils Four properties exercise haversine_distance / significant_movement?: - non-negativity across any in-range coordinate pair - symmetry: d(a, b) == d(b, a) within a tight tolerance - identity: d(p, p) == 0.0 - significance agrees with (haversine > threshold) for any integer threshold in 1..1000 Total: +4 properties, coverage stays clean (dialyzer: 0 new errors). --- test/aprsme/geo_utils_test.exs | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/test/aprsme/geo_utils_test.exs b/test/aprsme/geo_utils_test.exs index e9199a1..5279589 100644 --- a/test/aprsme/geo_utils_test.exs +++ b/test/aprsme/geo_utils_test.exs @@ -1,5 +1,6 @@ defmodule Aprsme.GeoUtilsTest do use ExUnit.Case, async: true + use ExUnitProperties alias Aprsme.GeoUtils @@ -78,4 +79,54 @@ defmodule Aprsme.GeoUtilsTest do refute GeoUtils.significant_movement?(33.16961, -96.4921, 33.16962, nil) end end + + describe "property: haversine_distance" do + property "is always non-negative for numeric inputs" do + check all( + lat1 <- float(min: -89.0, max: 89.0), + lon1 <- float(min: -179.0, max: 179.0), + lat2 <- float(min: -89.0, max: 89.0), + lon2 <- float(min: -179.0, max: 179.0) + ) do + assert GeoUtils.haversine_distance(lat1, lon1, lat2, lon2) >= 0 + end + end + + property "is symmetric: d(a,b) == d(b,a)" do + check all( + lat1 <- float(min: -89.0, max: 89.0), + lon1 <- float(min: -179.0, max: 179.0), + lat2 <- float(min: -89.0, max: 89.0), + lon2 <- float(min: -179.0, max: 179.0) + ) do + a = GeoUtils.haversine_distance(lat1, lon1, lat2, lon2) + b = GeoUtils.haversine_distance(lat2, lon2, lat1, lon1) + assert_in_delta a, b, 0.01 + end + end + + property "d(p, p) == 0" do + check all( + lat <- float(min: -89.0, max: 89.0), + lon <- float(min: -179.0, max: 179.0) + ) do + assert GeoUtils.haversine_distance(lat, lon, lat, lon) == 0.0 + end + end + end + + describe "property: significant_movement?" do + property "agrees with the haversine + threshold check" do + check all( + lat1 <- float(min: -89.0, max: 89.0), + lon1 <- float(min: -179.0, max: 179.0), + lat2 <- float(min: -89.0, max: 89.0), + lon2 <- float(min: -179.0, max: 179.0), + threshold <- integer(1..1000) + ) do + d = GeoUtils.haversine_distance(lat1, lon1, lat2, lon2) + assert GeoUtils.significant_movement?(lat1, lon1, lat2, lon2, threshold) == d > threshold + end + end + end end