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

iperf3-gui

A Flutter-based Android GUI wrapper for iperf3, the popular network performance measurement tool.

Features

  • Client-mode network throughput testing
  • Configurable server, port, duration and parallel streams
  • Live iperf3 output, streamed line by line while the test runs
  • Stop a running test early
  • Copy the transcript to the clipboard
  • iperf3 3.19 compiled into the app as a native library — no external binary

Architecture

lib/
├── domain/
│   ├── models/          # TestConfiguration, Iperf3Result
├── data/
│   └── services/        # Iperf3Service (MethodChannel → JNI)
├── ui/
│   └── features/
│       └── test_run/    # TestRunScreen, TestRunViewModel
└── main.dart            # App entry point

android/app/src/main/
├── kotlin/…/IperfRunner.kt   # MethodChannel handler, runs tests off the platform thread
└── cpp/iperf_jni.c           # JNI bridge into the iperf3 API

See docs/README.md for the data flow, the native API surface and the design decisions behind them.

Why JNI instead of executing a binary

Android SELinux in Enforcing mode blocks execve() from the untrusted_app domain, so shipping an iperf3 ELF in assets and running it via Process.start() fails with "Permission denied". iperf3 is therefore compiled as a shared library (libiperf3jni.so) and called in-process over JNI.

assets/iperf3/ still holds the pre-compiled binaries from that earlier approach. They are not declared in pubspec.yaml, so they are not packaged into the APK, and nothing in the app reads them.

Building

Prerequisites:

  • Flutter 3.47+
  • Android SDK with platform android-36
  • JDK 17
  • NDK r28+ and CMake 3.22.1 (for the native build)
flutter pub get
flutter analyze
flutter test
flutter build apk --debug
adb install -r build/app/outputs/flutter-apk/app-debug.apk

Testing against a local server

iperf3 -s -p 5201                 # on a machine on the same network
ip -4 addr show                   # note its address

Enter that address in the app, then press GO.

Dependencies

  • Dart: provider
  • Native: iperf3 3.19 + cJSON, built by CMake for armeabi-v7a and arm64-v8a

License

MIT — bundled iperf3 3.19 is BSD-3-Clause licensed.

S
Description
No description provided
Readme MIT 2 MiB
2026-08-26 13:44:11 +00:00
Languages
C 70.4%
Makefile 11.5%
Dart 7.8%
C++ 2.6%
Roff 2.5%
Other 5.2%