CI: one test binary per crate, nextest, line tables only (#1213)

* Build: line tables only for workspace crates, no debug info for dependencies

* Tests: one integration-test binary per crate

* xtask ci: nextest, doctests, test build without examples, one wasm check
This commit is contained in:
aiongg
2026-10-23 12:43:34 -04:00
committed by GitHub
parent 0f4fffcf6c
commit f001f37906
47 changed files with 105 additions and 38 deletions
+1 -1
View File
@@ -136,7 +136,7 @@ Standard: craftrules `standards/never-crash.md`.
## 5. Quality gates (before every commit)
`cargo xtask ci` = fmt check, clippy `-D warnings`, tests, layering check, asset check, wasm
`cargo xtask ci` = fmt check, clippy `-D warnings`, tests (nextest when installed) and doctests, layering check, asset check, wasm
check. Commit after each landed arc with a task id in the message (`M1.2: smart guides`) and push
to `main`.
+5
View File
@@ -84,11 +84,16 @@ self_named_constructors = "allow"
large_enum_variant = "allow"
result_large_err = "allow"
# Line tables keep file and line in panics and backtraces; full debug info
# for every test binary and dependency would multiply the target dir size.
# The test profile inherits both settings.
[profile.dev.package."*"]
opt-level = 2
debug = false
[profile.dev]
opt-level = 1
debug = "line-tables-only"
[profile.release]
opt-level = 3
+6
View File
@@ -0,0 +1,6 @@
mod cli;
mod convert;
mod damaged;
// Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md).
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod mcp_roots;
@@ -1,6 +1,4 @@
//! `deckcraft-cli mcp` with the automation root flags, run as the real binary.
// Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md).
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use std::io::Write;
use std::path::PathBuf;
+2
View File
@@ -0,0 +1,2 @@
mod chart_native;
mod show_clicks;
@@ -2,8 +2,6 @@
//! protocol, files open and load only from inside the read root and are saved and exported only
//! inside the write root. Paths that leave a root (`..`, another folder, a sibling with the same
//! prefix, a link) are refused, and nothing is written outside.
// Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md).
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use std::path::{Path, PathBuf};
+3
View File
@@ -0,0 +1,3 @@
// Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md).
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod automation_roots;
+1
View File
@@ -0,0 +1 @@
mod decode;
+27
View File
@@ -0,0 +1,27 @@
mod anchor;
mod chart_embed;
mod comments;
mod corpus;
mod corpus_charts;
mod corpus_elements;
mod corpus_kinds;
mod corpus_media;
mod corpus_style;
mod corpus_themes;
mod fills;
mod kept_parts;
mod locks;
mod malformed;
mod math_ns;
mod notes;
mod ole;
mod ole_vml;
mod placeholders;
mod roundtrip;
mod sample;
mod smartart;
mod sniff;
mod table_styles;
mod tags;
mod transition_xml;
mod trigger_steps;
+3 -3
View File
@@ -16,7 +16,7 @@ PowerPoint opens and saves; Viewer = opens only).
| Format | PowerPoint | DeckCraft read | DeckCraft write | Fidelity / notes | Tested by |
|---|---|---|---|---|---|
| `.pptx` presentation | Editor | yes | yes | Broad (see below); an owned corpus of decks we author round-trips tables, charts, animations, media, sections, masters and layouts, comments, links and actions, custom shows, rotated groups, inherited placeholders, text effects and body layout, theme effects, fills, bevels, connectors, 3-D models and animation bounce end. Not measured against PowerPoint renders or decks from outside this repo | `crates/pptx/tests/corpus.rs`, `corpus_kinds.rs`, `corpus_elements.rs`, `corpus_style.rs`, `corpus_themes.rs`, `corpus_charts.rs`, `corpus_media.rs`, `roundtrip.rs`, `sample.rs`, `malformed.rs`, `sniff.rs`; opened in PowerPoint by hand 2026-10-06 |
| `.pptx` presentation | Editor | yes | yes | Broad (see below); an owned corpus of decks we author round-trips tables, charts, animations, media, sections, masters and layouts, comments, links and actions, custom shows, rotated groups, inherited placeholders, text effects and body layout, theme effects, fills, bevels, connectors, 3-D models and animation bounce end. Not measured against PowerPoint renders or decks from outside this repo | `crates/pptx/tests/it/corpus.rs`, `corpus_kinds.rs`, `corpus_elements.rs`, `corpus_style.rs`, `corpus_themes.rs`, `corpus_charts.rs`, `corpus_media.rs`, `roundtrip.rs`, `sample.rs`, `malformed.rs`, `sniff.rs`; opened in PowerPoint by hand 2026-10-06 |
| `.potx` template | Editor | yes (as a presentation) | no: written with the `.pptx` content type | `file.saveTemplate` is Save As under another name | none |
| `.ppsx` slide show | Editor | yes (as a presentation) | no: written as `.pptx` content | Handed over by the system (command line, file association) it starts the slide show, as in PowerPoint; File › Open opens it for editing. The macOS Finder event arrives with #432 | `crates/ui-egui/src/opening.rs` tests |
| `.pptm` / `.potm` / `.ppsm` macro-enabled | Editor | no | no | Should open and keep `vbaProject.bin` without running it | none |
@@ -77,7 +77,7 @@ the round trip even though DeckCraft doesn't model it.
| Presentation, slide size, sections (p14), custom shows | yes | yes | |
| Slide masters, layouts, placeholders and inheritance | yes | yes | |
| Themes: colour, font (Latin/EA/CS), effect schemes, background styles | yes | yes | Unknown theme parts kept |
| Notes slides and notes master | yes | yes | Header, footer, date and slide-number text on the notes master is kept. Edited in View ▸ Notes Master. Notes keep their run and paragraph formatting (bold, italic, underline, size, levels, indents, bullets), shown and kept when edited in the notes pane and Notes Page (`crates/pptx/tests/notes.rs`). PDF notes pages print them as plain text; levels and bullets inherited from a custom notes master aren't shown |
| Notes slides and notes master | yes | yes | Header, footer, date and slide-number text on the notes master is kept. Edited in View ▸ Notes Master. Notes keep their run and paragraph formatting (bold, italic, underline, size, levels, indents, bullets), shown and kept when edited in the notes pane and Notes Page (`crates/pptx/tests/it/notes.rs`). PDF notes pages print them as plain text; levels and bullets inherited from a custom notes master aren't shown |
| Handout master | yes | yes | Header, footer, date and slide-number text is kept. Edited in View ▸ Handout Master |
| Shapes: preset geometry (~180 presets), custom geometry, groups, connectors | yes | yes | Presets re-derived in `crates/geom/src/preset.rs` (and `preset/extra.rs`). Outlines, dashes, arrowheads and ink inside a group, and text, keep their size in points whatever the group's child-coordinate scale (#233). |
| Fills: solid, gradient, picture (stretch/tile/crop), pattern, group | yes | yes | |
@@ -108,7 +108,7 @@ the round trip even though DeckCraft doesn't model it.
### What would move the PPTX number
1. A larger owned corpus (corporate templates, SmartArt, chartex charts, EMF, embedded fonts,
every transition and animation) in a corpus repo. `crates/pptx/tests/corpus.rs` and
every transition and animation) in a corpus repo. `crates/pptx/tests/it/corpus.rs` and
`corpus_kinds.rs` already check open → save → reopen (parts and text) for decks we author:
tables, charts, animations, media, sections and masters/layouts among them. Still needed:
render checks and a PowerPoint reference render for each (owner runs PowerPoint).
+2 -2
View File
@@ -181,7 +181,7 @@ Detail in [file-format-parity.md](file-format-parity.md).
`Series.secondary_axis`) and is not written back when the chart is rebuilt.
An unchanged chart's embedded workbook now survives save (#335); editing the chart still
drops it rather than writing a stale spreadsheet. The workbook and other retained chart parts
also survive `.deckcraft` save/reopen (`crates/engine/tests/chart_native.rs`), with binary
also survive `.deckcraft` save/reopen (`crates/engine/tests/it/chart_native.rs`), with binary
payloads in bounded ZIP entries sharing the media load budget (`crates/format` tests).
- **Impact:** most business decks have charts; editing their data is routine.
- **Estimate:** 12–20 h. Data and elements are **beta blockers** (6–10 h).
@@ -215,7 +215,7 @@ of the effect gallery. Detail in [animation-parity.md](animation-parity.md). 8
shapes in two decks) does nothing; triggers that start on `onStopAudio` or `begin` conditions are
not read as triggers; click boxes are the shape's bounding box (a hexagon's corners count) and do
not follow a shape that a motion path has moved. Trigger sequences now keep their separate
click steps on PPTX save, including paragraph builds (`crates/pptx/tests/trigger_steps.rs`).
click steps on PPTX save, including paragraph builds (`crates/pptx/tests/it/trigger_steps.rs`).
- **Impact:** games built around sound effects on buttons, audio-triggered effects or moving
clickable shapes do not play as authored.
- **Estimate:** 4–8 h.
+2 -2
View File
@@ -54,7 +54,7 @@ Open P0 items: Contextual tabs (Shape Format, Picture Format, Table Design/Layou
| Application shell | Multiple open presentations | P1 | P |
| Application shell | Preferences (autosave, units, defaults) | P2 | P |
| Files | New blank presentation | P0 | D |
| Files | Native file format (open/save; chart package parts retained, `crates/engine/tests/chart_native.rs`) | P0 | D |
| Files | Native file format (open/save; chart package parts retained, `crates/engine/tests/it/chart_native.rs`) | P0 | D |
| Files | Open PowerPoint .pptx | P0 | D |
| Files | Save as PowerPoint .pptx | P0 | D |
| Files | Open/save .potx/.ppsx/.pptm variants (.potx/.ppsx open; saved with the .pptx content type; no .pptm) | P2 | P |
@@ -206,7 +206,7 @@ Open P0 items: Contextual tabs (Shape Format, Picture Format, Table Design/Layou
| Animations | Effect options, start (on click/with/after), duration, delay | P0 | D |
| Animations | Animation pane (reorder, timing) | P0 | P |
| Animations | By-paragraph text builds | P1 | D |
| Animations | Triggers (separate click steps and paragraph builds round-trip; `crates/pptx/tests/trigger_steps.rs`) | P2 | D |
| Animations | Triggers (separate click steps and paragraph builds round-trip; `crates/pptx/tests/it/trigger_steps.rs`) | P2 | D |
| Animations | Animation Painter | P2 | D |
| Animations | Preview in editor | P1 | D |
| Slide show | Full-screen show from start / current | P0 | D |
+2 -2
View File
@@ -15,7 +15,7 @@ points and lead the beta plan.
| Core workflow | Works end to end? | Evidence | Hours to full pass |
|---|---|---|---|
| Build a deck from scratch (theme, layouts, titles and bullets, shapes, pictures from file), save, close and reopen it | yes | Commands for all of it; `.deckcraft` and `.pptx` round-trip tests (`crates/pptx/tests/roundtrip.rs`, `crates/format`); AutoRecover. Close discards edits without asking (#60, PR #75); image *paste* fails on Windows/Linux (#53), though insert-from-file works | 1–3 |
| Build a deck from scratch (theme, layouts, titles and bullets, shapes, pictures from file), save, close and reopen it | yes | Commands for all of it; `.deckcraft` and `.pptx` round-trip tests (`crates/pptx/tests/it/roundtrip.rs`, `crates/format`); AutoRecover. Close discards edits without asking (#60, PR #75); image *paste* fails on Windows/Linux (#53), though insert-from-file works | 1–3 |
| Open a colleague's `.pptx`, edit it, save it back as `.pptx` that PowerPoint opens | partial (not blocking) | The reader is lenient and keeps unknown objects. Files open in PowerPoint without repair (checked 2026-10-06). But only generated decks have been tested; text inside EMF/WMF pictures is missing; comment threads, media options and rewind are lost (#70, #74, #77) | 31–55 |
| Present: full-screen show with transitions, animations, media, presenter view with notes, next slide and timer | yes | 48/48 transitions, 64 effects, triggers, builds, media playback, presenter window, See All Slides and zoom. The presenter timer pauses and resets, and notes have a size control. A custom show plays only its slides. Typing a slide number during the show shows it, and Enter goes there. Presenter view isn't placed on the second display automatically (drag it there; #433) | 2–4 |
| Add business content: tables, charts with their data, simple diagrams | partial (not blocking) | Tables with styles, merge/split, all D or P. Charts insert with the common types and edit data in the Chart Data dialog (P0 partial); SmartArt is basic (pre-drawn shapes) | 12–20 |
@@ -26,7 +26,7 @@ points and lead the beta plan.
1. **Stop losing work and crashing:** land Close's save prompt (#60, PR #75), the Windows GPU
startup crashes (#40, #59), image paste on Windows/Linux (#53). 6–12 h.
2. **PPTX on real decks:** the owned corpus (`crates/pptx/tests/corpus*.rs`) round-trips decks we
2. **PPTX on real decks:** the owned corpus (`crates/pptx/tests/it/corpus*.rs`) round-trips decks we
author: tables, charts, animations, media, sections, masters and layouts, comments, links and
actions, custom shows, rotated groups, inherited placeholders, text effects and body layout,
theme effects, fills, bevels, connectors, 3-D models and animation bounce end, and keeps notes-
+51 -24
View File
@@ -13,7 +13,7 @@ mod stats;
mod version;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitCode};
use std::process::{Command, ExitCode, Stdio};
const USAGE: &str = "\
usage: cargo xtask <command>
@@ -22,8 +22,10 @@ commands:
assets check that every icon/image/font/asset is attributed in ATTRIBUTION.md
pdf-deps verify pinned writer sources and the production feature boundary
layers enforce the crate dependency layering (plan/architecture.md §3)
wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates
ci fmt --check, pdf-deps, clippy -D warnings, test, assets, layers, wasm (stops at first failure)
wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates (one call)
test workspace tests (cargo nextest when installed, else cargo test), then doctests
ci fmt --check, pdf-deps, clippy -D warnings, test, doctest, assets, layers, wasm
(stops at first failure)
stats [--exact] count tests and lines per crate (--exact: ask the test harness via `-- --list`)
bench [--save] [DECK...]
time open, render, save and export of the sample (and DECKs) in release, and
@@ -41,6 +43,7 @@ fn main() -> ExitCode {
Some("layers") => cmd_layers(),
Some("pdf-deps") => pdf_deps::run(&root()),
Some("wasm") => cmd_wasm(),
Some("test") => cmd_test().and_then(|()| cmd_doctest()),
Some("ci") => cmd_ci(),
Some("stats") => stats::run(&root(), rest.contains(&"--exact")),
Some("parity") => parity::run(&root()),
@@ -136,21 +139,51 @@ fn wasm_set() -> Result<Vec<String>, String> {
.collect())
}
/// One `cargo check` for the whole set: each crate is checked once, and
/// `--keep-going` still reports every failing crate. Features unify across the
/// set, so each crate is checked with the union of the features the set enables.
fn cmd_wasm() -> Result<(), String> {
let set = wasm_set()?;
let mut results = Vec::new();
if set.is_empty() {
return Err("no wasm-safe crates found in the workspace metadata".into());
}
let mut c = cargo();
c.args(["check", "--target", "wasm32-unknown-unknown", "--keep-going"]);
for pkg in &set {
let mut c = cargo();
c.args(["check", "--target", "wasm32-unknown-unknown", "-p", pkg]);
let ok = run(c, &format!("cargo check --target wasm32-unknown-unknown -p {pkg}")).is_ok();
results.push((pkg.clone(), ok));
c.args(["-p", pkg]);
}
println!("\nwasm32-unknown-unknown check:");
for (p, ok) in &results {
println!(" {:<6} {p}", if *ok { "ok" } else { "FAIL" });
}
let failed = results.iter().filter(|r| !r.1).count();
if failed == 0 { Ok(()) } else { Err(format!("{failed} crate(s) failed the wasm check")) }
run(c, &format!("cargo check --target wasm32-unknown-unknown --keep-going -p {}", set.join(" -p ")))?;
println!("\nwasm32-unknown-unknown check: ok ({} crates: {})", set.len(), set.join(", "));
Ok(())
}
/// Test targets for the test build. Examples are left out so they aren't
/// linked; clippy `--all-targets` still compiles them.
const TEST_TARGETS: [&str; 4] = ["--workspace", "--lib", "--bins", "--tests"];
fn nextest_available() -> bool {
cargo().args(["nextest", "--version"]).stdout(Stdio::null()).stderr(Stdio::null()).status().is_ok_and(|s| s.success())
}
fn cmd_test() -> Result<(), String> {
let mut c = cargo();
let tool = if nextest_available() {
c.args(["nextest", "run"]);
"nextest run"
} else {
eprintln!("note: cargo-nextest is not installed, running cargo test (cargo install cargo-nextest --locked)");
c.arg("test");
"test"
};
c.args(TEST_TARGETS);
run(c, &format!("cargo {tool} {}", TEST_TARGETS.join(" ")))
}
/// nextest does not run doctests.
fn cmd_doctest() -> Result<(), String> {
let mut c = cargo();
c.args(["test", "--workspace", "--doc"]);
run(c, "cargo test --workspace --doc")
}
fn cmd_ci() -> Result<(), String> {
@@ -169,18 +202,12 @@ fn cmd_ci() -> Result<(), String> {
"clippy",
Box::new(|| {
let mut c = cargo();
c.args(["clippy", "--workspace", "--all-targets", "--", "-D", "warnings"]);
run(c, "cargo clippy --workspace --all-targets -- -D warnings")
}),
),
(
"test",
Box::new(|| {
let mut c = cargo();
c.args(["test", "--workspace"]);
run(c, "cargo test --workspace")
c.args(["clippy", "--workspace", "--all-targets", "--keep-going", "--", "-D", "warnings"]);
run(c, "cargo clippy --workspace --all-targets --keep-going -- -D warnings")
}),
),
("test", Box::new(cmd_test)),
("doctest", Box::new(cmd_doctest)),
("assets", Box::new(|| assets::run(&root()))),
("layers", Box::new(cmd_layers)),
("wasm", Box::new(cmd_wasm)),