- docs/coverage/: COVERAGE.md (methodology, exclusions, landmines) + llvm-cov report + LCOV export. Union of engine (unit+integration) and headless UI layers over the real release apps: 95.71% lines / 94.59% regions / 97.88% functions across the three ports (149 tests green). - patches/0002-hub-graceful-child-shutdown.patch: hub closes the child app's stdin (EOF -> clean exit -> LLVM profile flush) before falling back to SIGKILL; without it app-side coverage reads 0%. - setup_makepad.sh: pin MAKEPAD_SHA, fatal patch checks, apply both patches, rustup default toolchain 1.98.0. - koboyo: FIX RefCell double-borrow crash in the Zoom-to-fit handler (read viewport before canvas_mut; found by the new UI test, app died with exit 101 'RefCell already borrowed' at widgets/widget.rs:1168). - New tests: koboyo +5 integration (key->tool map, kind_for_tool arms, delete/undo, content_bbox/zoom_to_fit, lasso) +4 UI (freehand gestures, select/move/delete + history keys, wheel zoom, ten element kinds + fit, rail lock/connect/export); rider +1 integration +4 UI (account rows, crosshair/Now pill, found-back/call/cancel, send button); insurance +2 integration (SheetTab tables, SheetDrag clamps).
131 lines
7 KiB
Markdown
131 lines
7 KiB
Markdown
# 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 <crate> --lib --test integration` | `model.rs` pure state machines (no Makepad runtime) |
|
|
| UI | `cargo test -p <crate> --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=<dir>/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 <lib-test-bin> --object <integration-bin> --object <ui-bin> ... \
|
|
--object target/release/makepad-example-<crate> \
|
|
--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/<tc>/lib/rustlib/<target>/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=<crate>/target` for the child build; symlink each
|
|
`examples/<crate>/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.
|