Regressietest structureel rood na --opnemen: red-loop, grensroute, hasselt-maastricht #40

Closed
opened 2026-08-14 23:34:12 +02:00 by jelmer · 1 comment
Owner

Gevonden tijdens het bouwen van issue #39 (pontjes-detectie), dus losstaand van die feature — bevestigd door de pontjes-code volledig weg te laten en het probleem identiek te reproduceren.

Symptoom

Na python3 tests/regressie.py --opnemen gevolgd door python3 tests/regressie.py (verify, geen netwerk) geven drie routes altijd hetzelfde verschil, elke keer opnieuw reproduceerbaar (twee keer getest, exact dezelfde waarden):

red-loop: VERSCHIL
    .hits[27].afstand: '2' → '1'
hasselt-maastricht: VERSCHIL
    .hits[15].afstand: '282' → '281'
grensroute: VERSCHIL   (geen detail — dieper dan de diep=6-afkap in verschillen())

Dit is geen thread-race of live-databron-drift: bij herhaling exact dezelfde waarden, en het reproduceert ook los van elke code-wijziging (kale checkout van 5fee11a).

Hypothese (vrij zeker)

opnemen() in tests/regressie.py schrijft de gouden bestanden vanuit de live pass (resultaten = draai_alles(), volle GPS-precisie), terwijl de fixture zelf coördinaten afrondt op 6 decimalen (_rond_floats, voor bestandsgrootte). verify speelt die afgeronde fixture terug. Op de meeste meldingen maakt 11 cm afronding niets uit, maar bij een enkele randgeval-melding (net op de rand van een dichtstbijzijnde-segment-berekening) verschuift dat de afstand met ~1 m — precies zo'n verschil als hierboven.

De docstring van _rond_floats zegt zelf al: "de gouden bestanden zijn tegen déze fixture geregenereerd, dus de vergelijking blijft exact" — maar de implementatie van opnemen() doet dat niet: die gebruikt de live-resultaten, niet een replay van de zojuist geschreven fixture.

Fix geprobeerd, maar niet doorgezet

Ik heb opnemen() aangepast om na het schrijven van de fixture de gouden bestanden te regenereren via terugspelen() (replay) in plaats van de live pass. Dat loste de twee hits[].afstand-verschillen op, maar onthulde een dieper, zeldzamer probleem: bij minstens één Komoot-bronroute (elfstedentocht of grensroute) verschilt ook de tegelselectie (points_public/12/2113/1327 ontbrak bij replay) — de routecoördinaten komen zelf ook uit een fixture-response (Komoot) die is afgerond, en bij toeval landde één punt net over een z12-tegelgrens. Dat brak de assert die ik toevoegde (onverwacht gemist bij de eigen zojuist geschreven fixture).

Ik heb die wijziging teruggedraaid (git checkout -- tests/regressie.py) in plaats van door te bouwen — dit raakt de testharness-architectuur en verdient een bewuste keuze, niet een snelle patch onderweg naar iets anders.

Impact

De deploy-gate in .forgejo/workflows/deploy.yml draait deze test; zolang dit niet is opgelost faalt elke deploy na een --opnemen, ongeacht wat er verder wijzigt.

Opties voor de oplossing

  1. Golden bestanden altijd via replay regenereren (zoals geprobeerd), plús de tegelselectie ongevoelig maken voor de afronding (bv. iets ruimere marge in tegels_langs_route, of Komoot-coördinaten niet afronden in de fixture).
  2. _rond_floats fijner maken (meer decimalen) zodat dit verschil in de praktijk nooit optreedt — verliest een deel van de bestandsgrootte-winst.
  3. De test tolerant maken voor ≤1-2 m verschil in afstand (numerieke tolerantie i.p.v. exacte JSON-match) — pragmatisch, maar vervaagt het "exact hetzelfde"-contract van de test.
**Gevonden tijdens het bouwen van issue #39** (pontjes-detectie), dus losstaand van die feature — bevestigd door de pontjes-code volledig weg te laten en het probleem identiek te reproduceren. **Symptoom** Na `python3 tests/regressie.py --opnemen` gevolgd door `python3 tests/regressie.py` (verify, geen netwerk) geven drie routes altijd hetzelfde verschil, elke keer opnieuw reproduceerbaar (twee keer getest, exact dezelfde waarden): ``` red-loop: VERSCHIL .hits[27].afstand: '2' → '1' hasselt-maastricht: VERSCHIL .hits[15].afstand: '282' → '281' grensroute: VERSCHIL (geen detail — dieper dan de diep=6-afkap in verschillen()) ``` Dit is **geen thread-race of live-databron-drift**: bij herhaling exact dezelfde waarden, en het reproduceert ook los van elke code-wijziging (kale checkout van `5fee11a`). **Hypothese (vrij zeker)** `opnemen()` in `tests/regressie.py` schrijft de gouden bestanden vanuit de **live** pass (`resultaten = draai_alles()`, volle GPS-precisie), terwijl de fixture zelf coördinaten afrondt op 6 decimalen (`_rond_floats`, voor bestandsgrootte). `verify` speelt die **afgeronde** fixture terug. Op de meeste meldingen maakt 11 cm afronding niets uit, maar bij een enkele randgeval-melding (net op de rand van een dichtstbijzijnde-segment-berekening) verschuift dat de afstand met ~1 m — precies zo'n verschil als hierboven. De docstring van `_rond_floats` zegt zelf al: *"de gouden bestanden zijn tegen déze fixture geregenereerd, dus de vergelijking blijft exact"* — maar de implementatie van `opnemen()` doet dat niet: die gebruikt de live-resultaten, niet een replay van de zojuist geschreven fixture. **Fix geprobeerd, maar niet doorgezet** Ik heb `opnemen()` aangepast om na het schrijven van de fixture de gouden bestanden te regenereren via `terugspelen()` (replay) in plaats van de live pass. Dat loste de twee `hits[].afstand`-verschillen op, maar onthulde een dieper, zeldzamer probleem: bij minstens één Komoot-bronroute (`elfstedentocht` of `grensroute`) verschilt ook de **tegelselectie** (`points_public/12/2113/1327` ontbrak bij replay) — de routecoördinaten komen zelf ook uit een fixture-response (Komoot) die is afgerond, en bij toeval landde één punt net over een z12-tegelgrens. Dat brak de assert die ik toevoegde (`onverwacht gemist bij de eigen zojuist geschreven fixture`). Ik heb die wijziging teruggedraaid (`git checkout -- tests/regressie.py`) in plaats van door te bouwen — dit raakt de testharness-architectuur en verdient een bewuste keuze, niet een snelle patch onderweg naar iets anders. **Impact** De deploy-gate in `.forgejo/workflows/deploy.yml` draait deze test; zolang dit niet is opgelost faalt elke deploy na een `--opnemen`, ongeacht wat er verder wijzigt. **Opties voor de oplossing** 1. Golden bestanden altijd via replay regenereren (zoals geprobeerd), plús de tegelselectie ongevoelig maken voor de afronding (bv. iets ruimere marge in `tegels_langs_route`, of Komoot-coördinaten niet afronden in de fixture). 2. `_rond_floats` fijner maken (meer decimalen) zodat dit verschil in de praktijk nooit optreedt — verliest een deel van de bestandsgrootte-winst. 3. De test tolerant maken voor ≤1-2 m verschil in `afstand` (numerieke tolerantie i.p.v. exacte JSON-match) — pragmatisch, maar vervaagt het "exact hetzelfde"-contract van de test.
Author
Owner

Opgelost in 73d76d2: opname-wrapper geeft nu de afgeronde bytes terug aan de aanroepende code (net als terugspelen() al deed), dus opnemen() rekent zelf al met exact dezelfde precisie als een latere --verify. Eén pass, geen dubbele netwerklast, geen los randgeval meer. Drie keer achter elkaar getest: --opnemen + verify → geslaagd.

Opgelost in 73d76d2: opname-wrapper geeft nu de afgeronde bytes terug aan de aanroepende code (net als terugspelen() al deed), dus opnemen() rekent zelf al met exact dezelfde precisie als een latere --verify. Eén pass, geen dubbele netwerklast, geen los randgeval meer. Drie keer achter elkaar getest: --opnemen + verify → geslaagd.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jelmer/gpx-afsluitingen#40
No description provided.