nigig-org/crates/apps/spreadsheet/spreadsheet-ui/tests/ui.rs
andodeki 8325805e22 test(spreadsheet): measure every test binary, and cover what that exposed
The coverage report was reading one object file. Cargo builds each
integration test into its own executable, so measuring only the lib-test
binary discarded everything `tests/` exercised.

That is not a rounding error. `persistence.rs` reported 41.77% with 14 of
its 17 functions apparently never called, while `tests/sync_flow.rs` was
calling `save_spreadsheet_state` and `load_saved_spreadsheet_state` on
every run and passing. The functions were covered; the report was reading
the wrong object. Fixing it alone moved persistence.rs to 72.15% and the
engine total 90.09% -> 90.57% without a single new test.

This is the third defect of its kind in this script — the ignore regex
that excluded the sources being measured, the awk matcher that never
fired, and now the single-object report. All three had the same
signature: a confident number that was measuring less than it claimed.

`--all-targets` for the UI crate too, so a future `tests/` file is
measured the day it is added rather than silently skipped. Doing that
immediately surfaced `spreadsheet-ui/tests/ui.rs`, which had **never
compiled**: the crate did not enable `makepad-widgets`' `test` feature,
so `makepad_widgets::makepad_test` did not resolve. `cargo test --lib`
never built it and nothing reported the breakage. The manifest now
enables the feature, matching `pdf-makepad`, and the two tests are
`#[ignore]`d with the same documented reason as `pdf-makepad`'s — the
fork has no headless Linux backend. Compiled on every run, so they
cannot rot further while appearing to be coverage.

Then the branches the corrected report named:

- `undo.rs` 86.11% -> 98.34%. Resize undo/redo, both directions. The
  `None` arms are the substance: a column with no width override must
  have its key *removed* on undo, not have a default written into it.
  Writing a default looks identical until the default changes, at which
  point every previously-resized-then-undone column stops following it.
- `persistence.rs` -> 93.70%. The legacy `current.sheet.csv` fallback,
  including that a whitespace-only current file must not shadow a real
  legacy one; `save_spreadsheet_state_as` writing where it says it does;
  and a rejected filename writing nothing at all.
- `style.rs` 92.19% -> 100%. Format and alignment codes round-trip, and
  the codes are distinct — a shared code passes a round-trip test while
  making two formats indistinguishable on disk.
- `model.rs` 84.87% -> 90.99%. Undo/redo intents, from_parts/into_parts,
  the active-sheet accessors agreeing with each other, and the disk
  round trip (`#[ignore]`d: it writes the shared generated/ file).

Verified by mutation, seven injected defects, each confirmed red:

  undo None-arm writes a default      6 fail
  two number formats share a code     1 fail
  save_as ignores its validation      1 fail
  legacy fallback removed             2 fail
  Undo intent wired to redo()         1 fail
  from_parts drops the active index   1 fail
  active_sheet_data_mut hits sheet 0  1 fail

Two of those changed the tests rather than merely passing:

- `save_as ignores its validation` really does write `../escape.tsv`
  into the crate root, and the file survives the failing run — so every
  later run failed on the previous run's debris rather than on the
  current code. The test now removes any leftover before asserting.
- `undo_and_redo_intents_reach_the_workbook` failed on first run because
  `apply()` does not call `begin_recording` and `apply_batch()` does, so
  there was nothing to undo. That asymmetry is the trap pinned by the
  engine's `only_set_cell_records_its_own_undo_step`; it now has a test
  on the model side too, since a caller reaching for `apply` and then
  offering an undo button gets a button that does nothing.

Also fixed a pre-existing clippy **error** in `util.rs` — `approx_constant`
on a literal `3.14` in a test that has nothing to do with PI. Confirmed
pre-existing by reproducing on a stashed tree. It denies the whole crate,
so no clippy gate could be added while it stood.

Engine 269 -> 280 tests, 90.09% -> 91.58%; floor 90 -> 91.
UI 17 -> 23 tests, 94.55% -> 95.70%; floor 94 -> 95.
2026-08-17 04:42:28 +00:00

83 lines
3.5 KiB
Rust

//! UI tests driving the spreadsheet workspace through Makepad event
//! delivery.
//!
//! **Currently `#[ignore]`d, for the same reason as `pdf-makepad`'s.** The
//! fork's `makepad_test` sets `MAKEPAD=headless` for the app it spawns, but
//! `platform/src/os/linux/windowing_backend.rs` only knows `X11` and
//! `Wayland` — there is no headless backend, so the app selects X11, finds
//! no display and dies. The full diagnosis is in
//! `REVIEWS/PDF_PARITY_PHASE1_STATUS.md`.
//!
//! Until this file was reached by `--all-targets` it had **never compiled**:
//! `spreadsheet-ui` did not enable `makepad-widgets`' `test` feature, so the
//! `makepad_widgets::makepad_test` import failed. `cargo test --lib` never
//! built it and nothing reported the breakage. The manifest now enables the
//! feature, matching `pdf-makepad`, so the file is compiled on every run and
//! cannot rot further while appearing to be coverage.
//!
//! Ignored rather than deleted or left failing on purpose: a red suite
//! everyone knows to disregard stops reporting the next real regression.
//! Remove the markers when the fork grows a headless backend.
//!
//! ```bash
//! cargo test -p spreadsheet-ui --test ui -- --ignored --test-threads=1
//! ```
use makepad_widgets::makepad_test::{run_with_config, Selector, TestApp, TestConfig};
fn run_ui_test(test_name: &str, body: impl FnOnce(TestApp)) {
let mut config = TestConfig::current_package(
env!("CARGO_MANIFEST_DIR"),
env!("CARGO_PKG_NAME"),
format!("ui::{test_name}"),
)
.expect("makepad test configuration");
// StudioHub launches the package through a child shell. Explicitly pass
// the isolated Cargo path so the child can find the same toolchain as the
// outer `cargo test` process.
if let Ok(path) = std::env::var("PATH") {
config.env.insert("PATH".into(), path.clone());
for directory in path.split(':') {
let cargo = std::path::Path::new(directory).join("cargo");
if cargo.is_file() {
config
.env
.insert("CARGO".into(), cargo.to_string_lossy().into_owned());
break;
}
}
}
for key in ["CARGO_HOME", "RUSTUP_HOME", "RUSTC"] {
if let Ok(value) = std::env::var(key) {
config.env.insert(key.into(), value);
}
}
// The current Makepad Linux branch has an incomplete `MAKEPAD=headless`
// cfg path. Let Linux compile its native backend; the Studio hub still
// drives the app through the test protocol.
config.env.remove("MAKEPAD");
run_with_config(config, body).expect("Makepad app failed to start or test failed");
}
#[test]
#[ignore = "needs a Makepad headless backend; see REVIEWS/PDF_PARITY_PHASE1_STATUS.md"]
fn spreadsheet_workspace_smoke() {
run_ui_test("spreadsheet_workspace_smoke", |app: TestApp| {
app.locator(Selector::id("formula_input")).wait_visible();
app.locator(Selector::id("grid")).wait_visible();
app.locator(Selector::id("add_sheet_btn")).wait_visible();
});
}
#[test]
#[ignore = "needs a Makepad headless backend; see REVIEWS/PDF_PARITY_PHASE1_STATUS.md"]
fn spreadsheet_workspace_can_add_sheet() {
run_ui_test("spreadsheet_workspace_can_add_sheet", |app: TestApp| {
app.locator(Selector::id("add_sheet_btn"))
.wait_visible()
.click();
app.locator(Selector::id("sheet_status"))
.wait_text("Active Sheet: Sheet4 | AutoCalc On");
});
}