fix(poort): een laadfout wijst niet meer naar een onschuldige test — en de cache was het niet #815

Merged
brenno merged 6 commits from fix/testcache-signatuur-798 into main 2026-07-24 18:31:01 +00:00
Owner

Sluit het bruikbare deel van #798, en draait de aanleiding om: de kernelcache is de verkeerde verdachte.

De storing viel tijdens dit werk zelf, mét stack

In de dekkingsfase, op test/import/snappy_test.dart, test 5507 van 6528 — de gemelde signatuur letterlijk. De stack is beslissend:

dart:_internal/async_cast.dart 80:25   CastStreamSubscription._onData

Dat is Stream.cast<T>(). De enige plek in deze keten die zo cast is stream_channel/lib/src/multi_channel.dart:143:

_innerStreamSubscription = _inner!.stream.cast<List>().listen((message) {

MultiChannel multiplext meerdere logische kanalen over één verbinding, dus elk frame hoort een [id, inhoud]-lijst te zijn. Een kale List in een cast is voor de VM List<dynamic> — vandaar exact die tekst, en vandaar een stack met niets dan dart:async erboven. Er kwam dus een JSON-object op een lijn die alleen frames vervoert. In flutter test is dat de lijn tussen het gereedschap en het testproces (_pipeHarnessToRemote, flutter_platform.dart), JSON over een WebSocket.

Wat weerlegd is

Drie manieren om die cache te bederven, tegen een echte draai van deze suite:

Beschadiging Uitkomst
Gehalveerd groen
~2.800 bytes omgeklapt groen
Vervangen door een geldige dill uit een andere bronboom groen

De frontend server valt in alle drie de gevallen terug op een volledige compilatie. "Het bestand is stuk" houdt geen stand. De tabel staat in docs/CHECKS.md zodat niemand die proeven overdoet.

Wat de drie bekende voorvallen gemeen hebben is belasting, niet de cache: alle drie in de dekkingsfase, en de reproductie viel terwijl er een tweede flutter test op dezelfde machine liep. Drie is geen steekproef; de gooiende regel staat vast, de aanleiding niet, en zo staat het opgeschreven.

Wat er nu gebeurt

Elke flutter test in de Makefile schrijft náást het scherm een machineleesbaar rapport (--file-reporter json:build/test-report.json), en bij een rode suite leest tool/explain_suite_failure.dart dat. Het noemt de bestanden die niet geladen konden worden — mét de zin die er anders bij inschiet, dat de tests erin niet gedraaid hebben en niet meetellen — en zegt of dat de bekende kanaalstoring is of een echte laadfout.

Een zijkanaal, dus het kán niets wegpoetsen: de uitvoer stroomt onveranderd door en de afloop blijft die van de suite. Filteren over de uitvoer is bewust niet gedaan — een pipe kost flutter test zijn voortgangsregel en maakt van een draai duizenden regels.

De belangrijkste eigenschap is niet de geruststelling maar het omgekeerde. Een bestand dat niet compileert is óók een laadfout. Die wordt hier juist als échte fout benoemd, niet als "bekend" weggezet — dat is de eerste toets in test/explain_suite_failure_test.dart, met een ontbrekende main, een mislukte compilatie en een ongerelateerde type-cast. Stond die verkeerd, dan was dit hulpmiddel erger dan geen hulpmiddel.

Verder: make clean-test-cache als grover middel. Alleen build/test_cache, bewust niet flutter clean — daaronder staan de platformbuilds waar DARTCV_LIB de native OpenCV-bibliotheek vindt, en die weggooien laat de gezichtsdetectietests zichzelf weer overslaan. Dat is groen om de verkeerde reden.

Bovenstrooms

Twee reports, geen van beide dit:

  • flutter/flutter#49351 — dezelfde foutvórm, ook toegeschreven aan een meegedragen .dill.track.dill. In 2020 gesloten: het bleek een pakketversieverschil. De cache was daar óók de rode haring.
  • flutter/flutter#128563open. De zustercache van flutter test onder build/ wordt niet ongeldig als Flutter het formaat wijzigt, en de conclusie van de melder is letterlijk dat opruimen de enige remedie is en dat dat niet vanzelf duidelijk is.

Wat er niet in zit

Een preventieve vervaldatum op de cache. Overwogen omdat de bestandsnaam alleen de dart-defines en de frontend-opties hasht (getDefaultCachedKernelPath) en dus een SDK- of pakketwijziging overleeft — geschrapt zodra bleek dat de cache de oorzaak niet is. Dan koop je een hercompilatie voor een theorie.

En de aanleiding zelf. Die blijft open in #798.

Poort

make check groen op deze tak in een verse worktree: 6.539 tests, dekking 86,5%, per-bestandsvloer 0. Twee overgeslagen tests zijn de gezichtsdetectie, die in een worktree zonder platformbuild altijd zichzelf overslaat.

Elk van de zes toetsen is één keer rood gezien tegen een gemuteerde tool, Makefile en docs.

🤖 Generated with Claude Code

Sluit het bruikbare deel van #798, en draait de aanleiding om: **de kernelcache is de verkeerde verdachte.** ## De storing viel tijdens dit werk zelf, mét stack In de dekkingsfase, op `test/import/snappy_test.dart`, test 5507 van 6528 — de gemelde signatuur letterlijk. De stack is beslissend: ``` dart:_internal/async_cast.dart 80:25 CastStreamSubscription._onData ``` Dat is `Stream.cast<T>()`. De enige plek in deze keten die zo cast is `stream_channel/lib/src/multi_channel.dart:143`: ```dart _innerStreamSubscription = _inner!.stream.cast<List>().listen((message) { ``` `MultiChannel` multiplext meerdere logische kanalen over één verbinding, dus elk frame hoort een `[id, inhoud]`-**lijst** te zijn. Een kale `List` in een cast is voor de VM `List<dynamic>` — vandaar exact die tekst, en vandaar een stack met niets dan `dart:async` erboven. Er kwam dus een JSON-**object** op een lijn die alleen frames vervoert. In `flutter test` is dat de lijn tussen het gereedschap en het testproces (`_pipeHarnessToRemote`, `flutter_platform.dart`), JSON over een WebSocket. ## Wat weerlegd is Drie manieren om die cache te bederven, tegen een echte draai van deze suite: | Beschadiging | Uitkomst | | --- | --- | | Gehalveerd | groen | | ~2.800 bytes omgeklapt | groen | | Vervangen door een geldige dill uit een andere bronboom | groen | De frontend server valt in alle drie de gevallen terug op een volledige compilatie. "Het bestand is stuk" houdt geen stand. De tabel staat in `docs/CHECKS.md` zodat niemand die proeven overdoet. Wat de drie bekende voorvallen gemeen hebben is **belasting**, niet de cache: alle drie in de dekkingsfase, en de reproductie viel terwijl er een tweede `flutter test` op dezelfde machine liep. Drie is geen steekproef; de gooiende regel staat vast, de aanleiding niet, en zo staat het opgeschreven. ## Wat er nu gebeurt Elke `flutter test` in de Makefile schrijft náást het scherm een machineleesbaar rapport (`--file-reporter json:build/test-report.json`), en bij een rode suite leest `tool/explain_suite_failure.dart` dat. Het noemt de bestanden die niet **geladen** konden worden — mét de zin die er anders bij inschiet, dat de tests erin niet gedraaid hebben en niet meetellen — en zegt of dat de bekende kanaalstoring is of een echte laadfout. Een zijkanaal, dus het kán niets wegpoetsen: de uitvoer stroomt onveranderd door en de afloop blijft die van de suite. Filteren over de uitvoer is bewust niet gedaan — een pipe kost `flutter test` zijn voortgangsregel en maakt van een draai duizenden regels. **De belangrijkste eigenschap is niet de geruststelling maar het omgekeerde.** Een bestand dat niet compileert is óók een laadfout. Die wordt hier juist als échte fout benoemd, niet als "bekend" weggezet — dat is de eerste toets in `test/explain_suite_failure_test.dart`, met een ontbrekende `main`, een mislukte compilatie en een ongerelateerde type-cast. Stond die verkeerd, dan was dit hulpmiddel erger dan geen hulpmiddel. Verder: `make clean-test-cache` als grover middel. Alleen `build/test_cache`, bewust niet `flutter clean` — daaronder staan de platformbuilds waar `DARTCV_LIB` de native OpenCV-bibliotheek vindt, en die weggooien laat de gezichtsdetectietests zichzelf weer overslaan. Dat is groen om de verkeerde reden. ## Bovenstrooms Twee reports, geen van beide dit: - [flutter/flutter#49351](https://github.com/flutter/flutter/issues/49351) — dezelfde foutvórm, ook toegeschreven aan een meegedragen `.dill.track.dill`. In 2020 gesloten: het bleek een **pakketversieverschil**. De cache was daar óók de rode haring. - [flutter/flutter#128563](https://github.com/flutter/flutter/issues/128563) — **open**. De zustercache van `flutter test` onder `build/` wordt niet ongeldig als Flutter het formaat wijzigt, en de conclusie van de melder is letterlijk dat opruimen de enige remedie is en dat dat niet vanzelf duidelijk is. ## Wat er niet in zit Een preventieve vervaldatum op de cache. Overwogen omdat de bestandsnaam alleen de dart-defines en de frontend-opties hasht (`getDefaultCachedKernelPath`) en dus een SDK- of pakketwijziging overleeft — geschrapt zodra bleek dat de cache de oorzaak niet is. Dan koop je een hercompilatie voor een theorie. En de aanleiding zelf. Die blijft open in #798. ## Poort `make check` groen op deze tak in een verse worktree: 6.539 tests, dekking 86,5%, per-bestandsvloer 0. Twee overgeslagen tests zijn de gezichtsdetectie, die in een worktree zonder platformbuild altijd zichzelf overslaat. Elk van de zes toetsen is één keer rood gezien tegen een gemuteerde tool, Makefile en docs. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
`make check` viel op 24-07-2026 twee keer om met een laadfout op een
testbestand dat los gedraaid groen was, en beide keren was het weg na het
opruimen van build/test_cache — dezelfde boom, geen letter gewijzigd (#798).
De kostenpost is niet de storing zelf maar de uren van wie een gezonde test
gaat debuggen.

`make clean-test-cache` legt het recept vast. Bewust alleen build/test_cache
en niet `flutter clean`: daaronder staan de platformbuilds waar DARTCV_LIB de
native OpenCV-bibliotheek vindt, en zonder die slaan de gezichtsdetectietests
zichzelf over en staat de suite groen om de verkeerde reden.

Elke `flutter test` in dit bestand sluit nu af met $(ON_SUITE_FAILURE), dat bij
een rode suite de foutsignatuur laat noemen. Het praat eróver en niet
erdoorheen: er wordt niets afgevangen, onderdrukt of herschreven, de uitvoer
stroomt onveranderd door en de afloop blijft die van de suite. Een pipe zou
`flutter test` bovendien zijn voortgangsregel afnemen en er duizenden regels
van maken.

CARRIED_TEST_CACHE staat er omdat de eerste opzet de aanwijzing onder élke rode
test zette: `flutter test` schrijft die cache tijdens de draai, dus achteraf
kijken levert altijd "ja" op. Met `:=` en $(wildcard) rekent make hem uit bij
het inlezen, dus vóór de suite draait — en zwijgt de aanwijzing als de cache
niet meegedragen is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Er stond niets over in docs/CHECKS.md; het zat in de hoofden van wie het eerder
had meegemaakt (#798). Nu is er een sectie met de letterlijke melding erin,
zodat wie hem intikt hier landt en niet bij de test die genoemd wordt.

De drie weerlegde verklaringen staan er met opzet bij. Halveren, bytes
omklappen en vervangen door een geldige dill uit een andere bronboom zijn alle
drie tegen een echte draai geprobeerd, en `flutter test` ving ze alle drie op
en werd groen. De voor de hand liggende lezing ("het bestand is stuk") houdt
dus geen stand. Ze staan opgeschreven zodat niemand ze nog eens uitprobeert.

Wat wél vaststaat is uit de SDK zelf: `getDefaultCachedKernelPath` hasht alleen
de dart-defines en de frontend-opties in de bestandsnaam, niet de SDK-versie en
niet de pakketresolutie — hetzelfde cachebestand overleeft dus een upgrade. Dat
is een mechanisme en geen bewijs, en het staat zo opgeschreven. Bovenstrooms
staat flutter/flutter#128563 nog open over precies deze klacht bij de
zustercache van `flutter test`; flutter/flutter#49351 had dezelfde
foutsignatuur maar bleek een pakketversieverschil, niet de cache.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
De storing zelf laat zich onder `flutter test` niet naspelen — hij zit in het
laden van een testbestand, een laag onder de suite. Wat wél toetsbaar is, is de
tegenmaatregel, en die bestaat uit vier eigenschappen die stil kapot kunnen
gaan zonder dat iets anders rood wordt:

- géén `flutter test` in de Makefile ontsnapt aan de aanwijzing (tien nu);
- de `||`-tak houdt `exit 1` — zonder dat wordt een rode suite groen omdat het
  afdrukken van de aanwijzing lukte, wat erger is dan geen aanwijzing;
- CARRIED_TEST_CACHE blijft `:=` — met een luie `=` staat de tekst onder élke
  rode test en leest niemand hem meer;
- `clean-test-cache` blijft binnen build/test_cache — een `rm -rf build` neemt
  de platformbuilds mee, en daarmee de bibliotheek waar DARTCV_LIB aan hangt.

Elk van de vier is één keer rood gezien tegen een gemuteerde Makefile, en de
docs-toetsen ook: de weerlegde verklaringen staan er zodat niemand die proeven
overdoet, dus verdwijnen ze niet ongemerkt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
De oorzaak is niet gevonden, en `make clean-test-cache` vernietigt precies de
toestand waarin het misging — de volgende keer is dus de enige kans om verder
te komen dan nu (#798). Vier dingen om éérst weg te zetten: de volledige
uitvoer in plaats van de staart (de suitepositie en de omliggende testnamen
zijn het halve signaal), een kopie van de cache zelf, de genoemde test los met
`-v` zolang de cache er nog ligt, en de boom plus de toolchain — want een
pakket- of SDK-wijziging is het enige mechanisme dat nog op tafel ligt en dat
is achteraf onzichtbaar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tijdens deze poort viel de storing uit #798 zélf, mét stack. Die wijst niet
naar de kernelcache maar naar stream_channel/lib/src/multi_channel.dart:143,
waar de verbinding met `stream.cast<List>()` gelezen wordt. Elk frame daarop
hoort een `[id, inhoud]`-lijst te zijn; er kwam een JSON-object langs. Een kale
`List` leest de VM voor als `List<dynamic>` — vandaar precies die melding, en
vandaar een stack met niets dan dart:async erboven. Het is de lijn tussen
`flutter test` en het testproces (`_pipeHarnessToRemote`), niet die test en
niet de cache.

Dat maakte de vorige opzet onhoudbaar: die noemde de cache "de eerste
verdachte" en gebruikte een meegedragen cache als voorwaarde om iets te zeggen.
Geen van beide klopt nog.

Wat ervoor in de plaats komt is preciezer én goedkoper. Elke suiteaanroep
schrijft náást het scherm een machineleesbaar rapport (`--file-reporter
json:build/test-report.json`), en tool/explain_suite_failure.dart leest dat bij
een rode suite. Het noemt de bestanden die niet GELADEN konden worden — de
tests erin hebben niet gedraaid en tellen niet mee — en zegt of dat de bekende
kanaalstoring is of een echte laadfout. Een zijkanaal kan per definitie niets
wegpoetsen: de uitvoer stroomt onveranderd door en de afloop blijft die van de
suite. Een pipe was geen optie, die kost `flutter test` zijn voortgangsregel en
maakt er duizenden regels van.

De belangrijkste eigenschap is niet de geruststelling maar het omgekeerde: een
bestand dat niet compileert is óók een laadfout, en die wordt juist benoemd als
echte fout. Zou de verklaring die wegzetten als "bekend", dan is dit hulpmiddel
erger dan geen hulpmiddel. Dat is de eerste toets in het testbestand, met een
ontbrekende `main`, een mislukte compilatie en een ongerelateerde type-cast.

Wat de drie bekende voorvallen gemeen hebben is belasting: alle drie in de
dekkingsfase, en de reproductie viel terwijl er een tweede `flutter test` op
dezelfde machine liep. Drie is geen steekproef; de gooiende regel staat vast,
de aanleiding niet. Zo staat het ook in docs/CHECKS.md.

Elk van de zes toetsen is één keer rood gezien tegen een gemuteerde tool,
Makefile en docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(changelog): de dubbele kopie uit de cherry-pick weg
All checks were successful
scans / scans (pull_request) Successful in 3m16s
c0303b9a84
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brenno merged commit 31a5e4302e into main 2026-07-24 18:31:01 +00:00
brenno deleted branch fix/testcache-signatuur-798 2026-07-24 18:31:01 +00:00
Sign in to join this conversation.
No description provided.