2 Commits

Author SHA1 Message Date
mikkeli fc15897541 feat: add a headless entry point that runs a test with no UI
Separation that nothing exercises is separation that quietly stops being true,
so this runs a full test through Iperf3Service and Iperf3Session with
WidgetsFlutterBinding but no runApp: no widget tree, no render surface. If the
run path ever starts needing the UI, this stops working. An architecture test
additionally forbids it importing lib/ui.

It is also the accurate way to measure. A 4-stream run against a local server
reports 30.2% local CPU here, against 79.9% for a comparable run with the
transcript repainting alongside — the UI was costing roughly fifty points of
CPU on a phone already near CPU-bound at gigabit rates. Throughput figures
taken through the UI are therefore pessimistic, and this is the path to use
when the number matters.

Parameters come from --dart-define so a run needs no code edit:
  flutter run -t lib/main_headless.dart --dart-define=IPERF_SERVER=…

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 20:24:44 +09:00
mikkeli 0cf019379a test: fail the build when a layer boundary is crossed
The layering only holds if something checks it, so these tests read the real
import statements under lib/ and assert the direction: domain imports no
Flutter and nothing else in the package, data never reaches into ui and may
import flutter/services alone, and ui never names a concrete transport.

A final test asserts the set of directories under lib/ is exactly the three
layers, so a new one cannot appear and silently escape every rule above.

Each violation reports the offending file and import with the reason, since a
guard that only says "expected empty" costs more than it saves. Verified by
introducing a UI-to-data import and a Flutter import in domain and confirming
both fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 19:56:03 +09:00