Testing
What each kind of test in OmniAccelerANT, OxidANT (the Rust core) and AccelerANTgine (the C++ core) proves, the rules that keep a green run honest, how to run them, and what is not covered yet. Which lane runs which test is AGENTS.md § What each lane tests; this page is the why behind that table (written after the test audit of 2026-10-15).
What runs, and what it proves
| Kind | Where | What a pass means | Runs in |
|---|---|---|---|
| Dart unit and widget tests | test/, the plugin's test/ |
Page logic and state; the shipped settings files load and name assets that exist; every page lays out at a phone's, a tablet's and a desktop's size, at 150 % and 200 % text, and lands on its own route rather than the error page | Every lane on the Dart VM; the web lane in Chrome |
| Rust unit tests | OxidANT crates/*, src/ |
ObservANT's motion, arrival, clip and HTTP rules; the webcam engine on videotestsrc with the real model, headless; the detector on a real photo |
OxidANT's Linux and Windows lanes |
| Property tests | OxidANT crates/observant (mod properties) |
A rule holds for every input, not a few examples: no request path, however encoded, leaves the web root; a served byte range stays inside the file; a public host name is refused; a config that validates keeps its numbers in range | With the Rust unit tests, 256 random inputs per rule and run |
| C++ unit tests | AccelerANTgine Test/commit (97 gtests) |
The ONNX engine, the detector's decoding, the config loaders, the GStreamer pipeline; under ASan and UBSan, and once more under TSan | AccelerANTgine's Linux and Windows lanes |
| C++ fuzz tests | AccelerANTgine Test/fuzz (FuzzTest) |
The JSON and TOML config readers and the box decoder survive arbitrary input | Linux clang lane, about a second per property |
| Integration tests | integration_test/ |
The app starts in a real window; a test-pattern frame travels Rust → knt_push_frame → the Linux texture |
Linux under Xvfb; Windows through flutter drive |
| End-to-end | scripts/linux/observant/test-observant-package.sh, scripts/windows/observant/Test-ObservANTWindows.ps1 |
The installed .deb, AppImage, flatpak and MSI start, answer every endpoint, detect, notify, and a real headless Chrome plays at least 200 frames of the stream | web.yml's observant jobs; the Windows arm64 MSI on windows-11-arm |
| Clean host | the clean-host job of linux-x64.yml and linux-arm64.yml (ANTfrastructure's clean-host-package-check) |
The .deb installs with apt on a bare Ubuntu 26.04, every ELF it installs resolves, and the app stays up 20 s under Xvfb from the .deb, the AppImage and the unpacked tarball. Inside the image every library exists, so only a bare host shows a missing one: its first run caught the .deb lacking libegl1 (2026-10-16) |
Linux x64 and arm64, on the bare runner after the lane |
| Performance | integration_test/performance_test.dart; OxidANT's Criterion benches |
The app's frame build times at start, page switches and resizes; what a frame costs before the model, and a whole detection | Linux x64 (Flutter); OxidANT's Linux lanes |
| Gate suites | scripts/linux/tests/, OxidANT's scripts/linux/tests/, scripts/windows/tests/ |
The CI gates themselves: each suite plants the failure its gate exists for and expects it to go red | The lanes' code-quality batches, OxidANT's lint workflow, the Pester jobs |
Rules that keep a green run honest
- Nothing skips silently. A test that cannot run (no GPU, no model, a missing
GStreamer element) skips locally, but CI sets
KATAGLYPHIS_REQUIRE_GPU,_MODELand_GSTto 1 and the skip becomes a failure;cargo testhides a passing test's skip line, so nobody would see it otherwise. AccelerANTgine's Linux lane fails on any skipped ctest test, since ctest reports "100% tests passed" with skips in it, and UBSan reports are fatal (halt_on_error=1). - Coverage has floors, per part. Dart
lib/fails below 96 % (97.2 % on 2026-10-16). OxidANT's workspace fails below 70 %, and each crate below its own floor (COVERAGE_FLOORSin OxidANT'sci-container-steps.sh), so one well-tested crate cannot hide a weak one. Floors only move up. - Performance is judged against a median. Each Linux x64 run's frame timings, and each OxidANT bench, are compared with the median of the default branch's last 20 runs: above +25 % the run warns, and a stable value at double the median fails it once 3 earlier runs exist. A run that failed never joins the history (camera-streaming.md § Measuring performance).
- Mutation testing checks the checks. Coverage says a line ran, not that a test would
notice it change. cargo-mutants plants small bugs one at a time (a flipped comparison, a
deleted branch, a constant return) and reruns the tests: on ObservANT's network-facing code
(
web.rs,static_files.rs) they caught about 84 % of 145 compiling mutants on 2026-10-15, and 142 after tests for every gap it named. The arrival logic (motion.rs,presence.rs,status.rs) went from about 92 % to 375 of 379, and camera choice, config and webhook (camera.rs,config.rs,notify.rs) from about 90 % to 134 of 145, and frames, clips and stats (frame.rs,clip.rs,stats.rs,output.rs,precompress.rs) from 376 to 400 of 412; there the privacy filter's colour planes had never been checked. The capture loop (capture.rs) is the weakest, 87 of 124 at first and 98 now, because most of what is left needs a real camera or a loaded model. What is left elsewhere changes nothing a client can see, or reads a real camera device, which no CI host has. How to run it: OxidANT's AGENTS.md. - A new test proves it can fail. Each gate and property added on 2026-10-15 was run
against a planted bug first:
..let through the path check, an unclamped range end, a route no page answers, a skipped ctest test, a slowdown in the perf reports. A test that stays green against its own bug is not added.
Why a lane cannot run a test
The notes under AGENTS.md's lane table, moved here on 2026-10-15.
- Android and Web build no desktop runner, so the desktop rows do not apply
there; Android adds the plugin's JVM test and the emulator run. The image carries
the emulator and Chrome (hub CON50, owner 2026-10-01);
android.ymlopens/dev/kvmto the container and boots the AVD withandroid-avd.sh(since 2026-10-06). - ObservANT checks live with ObservANT packages, which only
web.ymlbuilds, since they wrap its web build. They run as root in:latestand drive headless Chrome against the installed service. The MSI's run in:winamd64as ContainerAdministrator, where awebrtcsrcviewer plays the installed exe. - The integration test runs on both Windows architectures by driving the app the
build made (
flutter drive --use-application-binary) — x64 from the-FlutterTargetbuildBuild-WindowsDebugApps.ps1makes on the Debug tree, arm64 fromBuild-WindowsArm64App.ps1 -FlutterTarget— becauseflutter test -d windowswould build with Flutter's MSVC default. - The mutation suite builds synthetic ELFs for the Linux closure gate; Windows has no ELF and no such gate. Pester tests PowerShell that only Windows lanes run.
- The plugin's C++ format gate needs only the sources, so one lane grades them for
every platform; Windows has no C++ format step. The coverage floor is the same kind of
check: one lane's
flutter test --coveragemeasures the same Dart everywhere. Since 2026-10-15 that is the Dart gate's own test run (the hub'sflutter_checks.sh --coverage), not a second one, which had cost 55 s on every push. It countslib/without the generated bridge and localizations, was 96.5 % on 2026-10-13 (62 % the morning before, ahead of the native Stream page and Rust camera view tests), and fails the batch below the--mininlane-native-linux.sh, which states it once and moves up as tests are added; a file no test imports is not in the report at all. - The rustc stamp lives in an ELF
.comment, which a Windows PE does not have. The web lane builds its wasm withbuild-weband the dated nightly it names (AGENTS.md § 4), not Cargokit. - The clean-host job is Linux's: the Windows lanes already check the runner outside
:winamd64(C ABI, import walk, launch smoke), Android's run is the emulator's, and the web lane's packages are ObservANT's, whose checks install them as well. A Debian 13 start is checked by hand on a Raspberry Pi (camera-streaming.md § Relocatable Linux bundles). - The frame timings run on Linux x64 alone: a profile build costs minutes, and one arch is enough
to see the Dart side get slower.
reusable-linux.ymlkeeps the default branch's last 20out/perf/frontend.jsonin.perf-baseline/and writes the comparison with their median into the run's summary: above +25 % it warns, and an average or 90th-percentile build time at more than double the median fails the lane, once 3 earlier runs exist (camera-streaming.md § Measuring performance). - The webcam frame test is Linux-only:
hasPushedFrameis the Linux plugin's, and the Windows drives runsimple_test.dart. - The web lane runs both Dart suites in Chrome since 2026-10-05 (
--test-platform chrometo the hub's gate); the VM-only files are@TestOn('vm')and still run on every native lane. Chrome still compiles them, so a VM-only test must compile for web too: a plainintfor a Rusti64field failed web run 38255398872 (AGENTS.md § 4,PlatformInt64Util.from).flutter test --platform chromein:latestshows it before a push. - Every lane runs the plugin's Dart tests through the same strict gate as the
app's:
run_flutter_common_checkson Linux, Plugin Flutter Tests inBuild-Windows.ps1,Invoke-FlutterTests.ps1on arm64. - A push to
developruns Linux x64 alone (since 2026-10-15, the family's rule in ANTfrastructure'sbranch-and-release-strategy.md). The other five lanes run when a commit message asks, with[ci windows],[ci arm64],[ci android],[ci web]or[ci all];on-request.ymlreads the tokens and calls those lanes. Every lane also has Run workflow (gh workflow run windows-x64.yml --ref develop). So a change to Windows-only code carries[ci windows], and a test that only one of the columns above runs is proved by asking for that lane. - Every push to
mainruns every lane throughrelease.yml, and releases only when all are green (project-operations.md § Release Hygiene). - A newer request for a lane cancels its older run on the same branch (each lane's
concurrencyhascancel-in-progress: true); a push without a token cancels nothing. Before 2026-10-15 every push started every lane, and on 2026-10-12 that cancelled 77 of the day's 142 push runs, about 18 hours of runner wall time.
Running them
# Dart: the app's suites, the plugin's, and the integration test (needs a display).
flutter test
(cd packages/kataglyphis_native_inference && flutter pub get && flutter test)
xvfb-run -a flutter test integration_test/simple_test.dart -d linux
# Frame timings in profile mode, and where a slow frame spends its time.
xvfb-run -a flutter drive --profile -d linux --driver=test_driver/perf_driver.dart \
--target=integration_test/performance_test.dart
python3 scripts/linux/frontend-perf-report.py out/perf/frontend.json
# Rust, as CI runs it (fetches the model, demands GPU, model and GStreamer), in :latest:
(cd third_party/OxidANT && bash scripts/linux/ci-container-steps.sh test)
# More random inputs per property, for a deeper look than CI's 256:
(cd third_party/OxidANT && PROPTEST_CASES=100000 cargo test -p kataglyphis_observant properties)
The plugin is its own package with its own lock, so the root's flutter test never reaches
its suite. flutter test --platform chrome shows before a push what the web lane will say;
it compiles every test file for Chrome, the VM-only ones too.
Known gaps
As found on 2026-10-15, so a green run is read for what it is:
- Tarpaulin does not measure feature-gated Rust code. Every module behind
#[cfg(feature = …)]is missing from its report,crates/mediaand the webcam engine among them, although their tests run.cargo llvm-covwould see them; the image does not carry it (OxidANT's AGENTS.md). - The native plugin's Windows and Android C++ is barely tested: one
GetPlatformVersiongtest on Windows, one JVM test on Android, and nothing for the Android GStreamer glue. The Linux texture has six gtests since 2026-10-15 (pipeline errors as Dart sees them, a transparent test pattern shown opaque, theknt_push_framecodes and registry), each checked against a planted bug. - Web-only Dart is outside the coverage figure.
webrtc_view.dartand the other files behinddart.library.js_interopnever load on the VM, and the Chrome run measures nothing; the ObservANT package test's real browser is what covers them. The one with the most to lose,observant_api_web.dart, through which the Camera page reads and saves ObservANT's settings, has a Chrome-only test since 2026-10-15 (observant_api_web_test.dart,@TestOn('browser')): it swaps the page'sfetchfor a fake and checks each request's URL, method, header, JSON body and timeout, and that an error status or a failed request reads as nothing. - ANThology's own tests run in its repository only, and a third of its
lib/files have none. Its sortable table, behind the blog overview here and four lists on the website, got its first tests on 2026-10-15, and they found three sorting bugs. - C++ fuzzing runs in unit mode only, about a second per property, without coverage
feedback. AccelerANTgine forces
FUZZTEST_FUZZING_MODEoff (third_party/CMakeLists.txt): FuzzTest's coverage flags recursed with AddressSanitizer. Its targets read config files and decode boxes; what a network peer sends is parsed in Rust, where the property tests above run. - Windows does not demand the model, so its real-model tests pass by skipping: its unit
tests run before the model is fetched (OxidANT's
BACKLOG.md). The riscv64 lane fetches and demands it since 2026-10-15.