# Test Coverage — makepad-ports (rider · koboyo · insurance) Measured 2026-09-01 with LLVM source-based coverage (`-C instrument-coverage`) on Rust 1.98.0 / cargo-llvm-cov 0.9.0 tooling, Makepad pinned at `b41e7404b6bb893d32f7d2924cd7b1c28983547f` + `patches/`. ## Results 149 tests, all green: 123 engine-layer (unit + integration) + 26 UI-layer (headless full-app). Coverage below is the **union of all three layers**, i.e. model files are counted from unit, integration *and* real app binaries. | File | Lines | Regions | Functions | |---------------------------|-----------:|--------:|----------:| | insurance/src/app.rs | **99.57%** | 98.98% | 100.00% | | insurance/src/model.rs | **100.00%**| 100.00% | 100.00% | | koboyo/src/app.rs | **88.85%** | 89.77% | 88.00% | | koboyo/src/canvas_view.rs | **82.48%** | 81.01% | 94.44% | | koboyo/src/model.rs | **99.09%** | 97.35% | 100.00% | | rider/src/app.rs | **98.19%** | 97.90% | 100.00% | | rider/src/model.rs | **100.00%**| 100.00% | 100.00% | | **TOTAL** | **95.71%** |**94.59%**| **97.88%**| Raw artifacts: [`coverage-report.txt`](coverage-report.txt) (llvm-cov table) and [`coverage.lcov`](coverage.lcov) (LCOV export, per-line data). ## Layer mapping | Layer | What runs | What it covers | |---|---|---| | Engine | `cargo test -p --lib --test integration` | `model.rs` pure state machines (no Makepad runtime) | | UI | `cargo test -p --test ui` (headless, `MAKEPAD=headless`) | `app.rs` + `canvas_view.rs` in the **real release app**, driven over the studio stdin protocol by the hub | ## Methodology 1. Everything is built with `cargo llvm-cov show-env` wrapper env sourced, `MAKEPAD=headless` exported for **every** cargo invocation (see landmine 3), and `LLVM_PROFILE_FILE=/cov-%p-%m.profraw` so every process — test harness, hub, and the spawned app — writes its own profile. 2. Test binaries build into a shared target dir; the UI harness spawns the release app via the studio hub (each crate's `target/` symlinked to a shared app target dir so the hub's child build cache-hits and stays inside the 600 s startup timeout). 3. Reporting drives the LLVM tools **directly** (see landmine 1): ```sh llvm-profdata merge -sparse profraw/*.profraw -o all.profdata llvm-cov report --instr-profile=all.profdata \ --object --object --object ... \ --object target/release/makepad-example- \ --ignore-filename-regex='.*/(platform|widgets|draw|libs|studio|...)/.*|.*/main\.rs$|.*/tests/.*' ``` ## Exclusions (deliberate) - **Makepad framework code** (`platform/`, `widgets/`, `draw/`, `libs/`, `studio/`): not ours; filtered by `--ignore-filename-regex`. - **`main.rs`** per crate: 3-line platform startup stub (`app_main!`), only reachable as a process entry point outside the harness. - **Generated Makepad code**: `live_design!` DSL blocks compile into registration code attributed to framework files, so the filter above already removes it; derived `#[derive(Live, LiveHook)]` impls that remain in our files are exercised by app startup in the UI layer. - **In-file `#[cfg(test)]` panic arms** (koboyo model.rs 1170/1176/1186): `panic!` branches of the crate's own unit-test assertions — unreachable when tests pass. - **Mobile-only chrome** (koboyo app.rs ~994-1020 dots-menu rows): the ⋮ `dots_btn` is `visible: false` at the desktop breakpoint the headless window uses, so the rows cannot be reached without viewport resizing support in the harness. ## Remaining uncovered (audited, not blind spots) - `koboyo/app.rs`: dots-menu rows (mobile-only, above), a few popup auto-close edges and `search_input` Enter-pick fallback rows. - `koboyo/canvas_view.rs`: draft-preview paint for shape kinds only reachable mid-gesture with a live draft of *every* kind (table/browser-frame/phone frame previews), plus the selection-handles paint path for zero-size picks. - `rider/app.rs`: chat list draw for the empty-history branch (933) and two timer-race guards (1091/1095, 1278-1279) — the reply/toast timers usually fire before the suite tears down; the guards need a cancel in the same tick. - `insurance/app.rs` 1554: toast-expiry timer race (same class). ## Test additions made for coverage (this tranche) - koboyo: +5 integration (full key→tool map, every `kind_for_tool` arm + badge counter, delete/undo, `content_bbox`/`zoom_to_fit`, lasso), +4 UI (freehand gestures pen/laser/lasso/eraser/hand, select-move-delete + ⌘Z/⇧⌘Z/⌘Y, wheel zoom, ten element kinds + zoom-to-fit, rail lock/connect/export). - rider: +1 integration (`NavEffect::none`), +4 UI (account rows via avatar, crosshair + Now pill, found-back + call + cancel-arriving, send button). - insurance: +2 integration (SheetTab tables, SheetDrag translate clamps). ### 🐛 Real bug found and fixed by the new tests `koboyo_element_kinds_and_zoom_to_fit` crashed the app (exit 101): **RefCell double-borrow** in the Zoom-to-fit handler — it read the canvas widget's area *inside* the `canvas_mut` closure that already held the same widget's `RefCell` mutably (`widgets/src/widget.rs:1168: RefCell already borrowed`). Fixed in `koboyo/src/app.rs` by reading the viewport before borrowing. The handler was previously 0%-covered — exactly the class of bug coverage work is supposed to surface. ## Landmines (read before re-running) 1. **`cargo llvm-cov report` omits example-crate objects** in this workspace (0.9.0 object discovery misses them even with profdata + binaries present). Drive `llvm-profdata`/`llvm-cov` directly with explicit `--object` flags — the toolchain's own copies live under `$RUSTUP_HOME/toolchains//lib/rustlib//bin/`. 2. **SIGKILL eats app coverage.** The hub terminates the child app with SIGKILL to the process group, so the LLVM runtime never flushes and `app.rs` reads 0%. `patches/0002-hub-graceful-child-shutdown.patch` closes the child's stdin first (EOF cleanly ends the headless stdin loop → atexit flush) and only escalates to SIGKILL after 5 s. 3. **Keep `MAKEPAD=headless` set for every build.** The platform build script re-runs when it changes, flipping the `headless` cfg and forcing a full rebuild of `makepad-platform`/`makepad-widgets` — on a small box that rebuild can OOM. Add disk-backed swap and build `-j1` if RAM < 4 GB. 4. **Login-shell PATH reset.** The hub spawns the app via `/bin/sh -lc`, which resets PATH; `cargo` must resolve on the *system* default PATH (e.g. symlinks in `/usr/local/bin`) and `RUSTUP_HOME`/`CARGO_HOME` must be exported. 5. **Per-crate target dirs.** The test runtime forces `CARGO_TARGET_DIR=/target` for the child build; symlink each `examples//target` to a shared pre-warmed release dir or the child build blows the 600 s startup timeout. 6. **Never run `cargo llvm-cov` inside its own `show-env` context** — it degrades to plain `cargo test` against the wrong target dir and rebuilds the world.