From e73b7a9ccb4bc61e54d1fdc61b5954879c637d00 Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Tue, 25 Aug 2026 13:26:22 +0200 Subject: Migrate cross-package ride test fixtures to the ridetest DSL Replace raw FromColumns 4-array blocks in official_climb, mountain_pass and web test rides with ridetest.MustRide strings. Add a Heading option to ridetest for routes whose points are spaced differently per kilometer (e.g. the mountain_pass expanded-bounds route at 0.1 degrees/km). Lap, direction and cluster tests in official_climb keep FromColumns: the DSL generates monotonic northeast coords and cannot express per-point loops or reversals. --- internal/ridetest/ride.go | 40 +++++++++++++++++++++++++++++++--------- internal/ridetest/ride_test.go | 11 +++++++++++ 2 files changed, 42 insertions(+), 9 deletions(-) (limited to 'internal/ridetest') diff --git a/internal/ridetest/ride.go b/internal/ridetest/ride.go index a6413a9..e74ce49 100644 --- a/internal/ridetest/ride.go +++ b/internal/ridetest/ride.go @@ -6,9 +6,10 @@ // ridetest.MustRide("2km at 7% | 1km at -7%") // // Options adjust the generated columns: Sampling (step in meters, default -// 100), StartCoord (first point; later points progress northeast by 0.01° of -// latitude and longitude per kilometer), StartTime (default the zero time, -// one second between points) and StartElevation (default 0). +// 100), StartCoord (first point; later points progress northeast at the +// Heading rate, default 0.01° of latitude and longitude per kilometer), +// StartTime (default the zero time, one second between points) and +// StartElevation (default 0). package ridetest import ( @@ -46,6 +47,8 @@ type point struct { type options struct { samplingM float64 startCoord *geodist.Coord + headingLat float64 + headingLon float64 startTime time.Time startElevM float64 } @@ -60,9 +63,28 @@ func Sampling(m float64) Option { } // StartCoord sets the coordinate of the first point. Each following point -// progresses northeast by 0.01° of latitude and longitude per kilometer. +// progresses northeast at the rate set by Heading, default 0.01° of latitude +// and longitude per kilometer. func StartCoord(lat, lon float64) Option { - return func(o *options) { o.startCoord = &geodist.Coord{Lat: lat, Lon: lon} } + return func(o *options) { + o.startCoord = &geodist.Coord{Lat: lat, Lon: lon} + if o.headingLat == 0 { + o.headingLat = headingPerKm + } + if o.headingLon == 0 { + o.headingLon = headingPerKm + } + } +} + +// Heading sets the latitude and longitude offset applied to each point per +// kilometer of ride, in degrees (default 0.01 northeast when StartCoord is +// set). Only meaningful together with StartCoord. +func Heading(latPerKm, lonPerKm float64) Option { + return func(o *options) { + o.headingLat = latPerKm + o.headingLon = lonPerKm + } } // StartTime sets the timestamp of the first point; each following point is @@ -80,7 +102,7 @@ func StartElevation(m float64) Option { // // Example: "2km at 7% | 1km at -7%". func Ride(dsl string, opts ...Option) (ride.Ride, error) { - o := options{samplingM: defaultSamplingM} + o := options{samplingM: defaultSamplingM, headingLat: headingPerKm, headingLon: headingPerKm} for _, opt := range opts { opt(&o) } @@ -250,10 +272,10 @@ func buildPoints(sections []section, o options) []point { lat, lon = o.startCoord.Lat, o.startCoord.Lon } // Coordinates derive from the total distance so that exact multiples of - // the per-kilometer heading (0.01° x km as float64) stay exact; a running - // accumulation drifts after a few points. + // the per-kilometer heading stay exact; a running accumulation drifts + // after a few points. coord := func(distanceM float64) geodist.Coord { - return geodist.Coord{Lat: lat + headingPerKm*(distanceM/1000), Lon: lon + headingPerKm*(distanceM/1000)} + return geodist.Coord{Lat: lat + o.headingLat*(distanceM/1000), Lon: lon + o.headingLon*(distanceM/1000)} } points := make([]point, 0, len(sections)+1) elevation := o.startElevM diff --git a/internal/ridetest/ride_test.go b/internal/ridetest/ride_test.go index 69c084d..65483f2 100644 --- a/internal/ridetest/ride_test.go +++ b/internal/ridetest/ride_test.go @@ -53,6 +53,17 @@ func TestRideStartCoordHeading(t *testing.T) { assert.InDelta(t, 5.12, r.Coords()[2].Lon, 1e-9) } +func TestRideHeadingOption(t *testing.T) { + r, err := Ride("1km at 0%", StartCoord(43.0, 5.0), Heading(0.1, 0.1), Sampling(1000)) + require.NoError(t, err) + + // A single kilometer at 0.1°/km ends at (43.1, 5.1), matching rides whose + // points are spaced 0.1° apart over a kilometer. + require.Len(t, r.Coords(), 2) + assert.InDelta(t, 43.1, r.Coords()[1].Lat, 1e-9) + assert.InDelta(t, 5.1, r.Coords()[1].Lon, 1e-9) +} + func TestRideStartTime(t *testing.T) { start := time.Date(2026, 8, 1, 10, 0, 0, 0, time.UTC) r, err := Ride("1km at 0%", StartTime(start), Sampling(500)) -- cgit v1.3