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, _MODEL and _GST to 1 and the skip becomes a failure; cargo test hides 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_FLOORS in OxidANT's ci-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.yml opens /dev/kvm to the container and boots the AVD with android-avd.sh (since 2026-10-06).
  • ObservANT checks live with ObservANT packages, which only web.yml builds, since they wrap its web build. They run as root in :latest and drive headless Chrome against the installed service. The MSI's run in :winamd64 as ContainerAdministrator, where a webrtcsrc viewer 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 -FlutterTarget build Build-WindowsDebugApps.ps1 makes on the Debug tree, arm64 from Build-WindowsArm64App.ps1 -FlutterTarget — because flutter test -d windows would 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 --coverage measures the same Dart everywhere. Since 2026-10-15 that is the Dart gate's own test run (the hub's flutter_checks.sh --coverage), not a second one, which had cost 55 s on every push. It counts lib/ 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 --min in lane-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 with build-web and 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.yml keeps the default branch's last 20 out/perf/frontend.json in .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: hasPushedFrame is the Linux plugin's, and the Windows drives run simple_test.dart.
  • The web lane runs both Dart suites in Chrome since 2026-10-05 (--test-platform chrome to 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 plain int for a Rust i64 field failed web run 38255398872 (AGENTS.md § 4, PlatformInt64Util.from). flutter test --platform chrome in :latest shows it before a push.
  • Every lane runs the plugin's Dart tests through the same strict gate as the app's: run_flutter_common_checks on Linux, Plugin Flutter Tests in Build-Windows.ps1, Invoke-FlutterTests.ps1 on arm64.
  • A push to develop runs Linux x64 alone (since 2026-10-15, the family's rule in ANTfrastructure's branch-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.yml reads 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 main runs every lane through release.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 concurrency has cancel-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/media and the webcam engine among them, although their tests run. cargo llvm-cov would see them; the image does not carry it (OxidANT's AGENTS.md).
  • The native plugin's Windows and Android C++ is barely tested: one GetPlatformVersion gtest 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, the knt_push_frame codes and registry), each checked against a planted bug.
  • Web-only Dart is outside the coverage figure. webrtc_view.dart and the other files behind dart.library.js_interop never 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's fetch for 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_MODE off (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.