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) ## 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 check. Commit after each landed arc with a task id in the message (`M1.2: smart guides`) and push
to `main`. to `main`.
+5
View File
@@ -84,11 +84,16 @@ self_named_constructors = "allow"
large_enum_variant = "allow" large_enum_variant = "allow"
result_large_err = "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."*"] [profile.dev.package."*"]
opt-level = 2 opt-level = 2
debug = false
[profile.dev] [profile.dev]
opt-level = 1 opt-level = 1
debug = "line-tables-only"
[profile.release] [profile.release]
opt-level = 3 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. //! `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::io::Write;
use std::path::PathBuf; 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 //! 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 //! 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. //! 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}; 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 | | 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 | | `.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 | | `.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 | | `.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 | | | Presentation, slide size, sections (p14), custom shows | yes | yes | |
| Slide masters, layouts, placeholders and inheritance | 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 | | 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 | | 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). | | 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 | | | 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 ### What would move the PPTX number
1. A larger owned corpus (corporate templates, SmartArt, chartex charts, EMF, embedded fonts, 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: `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: tables, charts, animations, media, sections and masters/layouts among them. Still needed:
render checks and a PowerPoint reference render for each (owner runs PowerPoint). 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. `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 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 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). payloads in bounded ZIP entries sharing the media load budget (`crates/format` tests).
- **Impact:** most business decks have charts; editing their data is routine. - **Impact:** most business decks have charts; editing their data is routine.
- **Estimate:** 12–20 h. Data and elements are **beta blockers** (6–10 h). - **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 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 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 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 - **Impact:** games built around sound effects on buttons, audio-triggered effects or moving
clickable shapes do not play as authored. clickable shapes do not play as authored.
- **Estimate:** 4–8 h. - **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 | Multiple open presentations | P1 | P |
| Application shell | Preferences (autosave, units, defaults) | P2 | P | | Application shell | Preferences (autosave, units, defaults) | P2 | P |
| Files | New blank presentation | P0 | D | | 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 | Open PowerPoint .pptx | P0 | D |
| Files | Save as 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 | | 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 | Effect options, start (on click/with/after), duration, delay | P0 | D |
| Animations | Animation pane (reorder, timing) | P0 | P | | Animations | Animation pane (reorder, timing) | P0 | P |
| Animations | By-paragraph text builds | P1 | D | | 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 | Animation Painter | P2 | D |
| Animations | Preview in editor | P1 | D | | Animations | Preview in editor | P1 | D |
| Slide show | Full-screen show from start / current | P0 | 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 | | 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 | | 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 | | 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 | | 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 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. 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 author: tables, charts, animations, media, sections, masters and layouts, comments, links and
actions, custom shows, rotated groups, inherited placeholders, text effects and body layout, 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- 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; mod version;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::{Command, ExitCode}; use std::process::{Command, ExitCode, Stdio};
const USAGE: &str = "\ const USAGE: &str = "\
usage: cargo xtask <command> usage: cargo xtask <command>
@@ -22,8 +22,10 @@ commands:
assets check that every icon/image/font/asset is attributed in ATTRIBUTION.md assets check that every icon/image/font/asset is attributed in ATTRIBUTION.md
pdf-deps verify pinned writer sources and the production feature boundary pdf-deps verify pinned writer sources and the production feature boundary
layers enforce the crate dependency layering (plan/architecture.md §3) layers enforce the crate dependency layering (plan/architecture.md §3)
wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates (one call)
ci fmt --check, pdf-deps, clippy -D warnings, test, assets, layers, wasm (stops at first failure) 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`) stats [--exact] count tests and lines per crate (--exact: ask the test harness via `-- --list`)
bench [--save] [DECK...] bench [--save] [DECK...]
time open, render, save and export of the sample (and DECKs) in release, and 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("layers") => cmd_layers(),
Some("pdf-deps") => pdf_deps::run(&root()), Some("pdf-deps") => pdf_deps::run(&root()),
Some("wasm") => cmd_wasm(), Some("wasm") => cmd_wasm(),
Some("test") => cmd_test().and_then(|()| cmd_doctest()),
Some("ci") => cmd_ci(), Some("ci") => cmd_ci(),
Some("stats") => stats::run(&root(), rest.contains(&"--exact")), Some("stats") => stats::run(&root(), rest.contains(&"--exact")),
Some("parity") => parity::run(&root()), Some("parity") => parity::run(&root()),
@@ -136,21 +139,51 @@ fn wasm_set() -> Result<Vec<String>, String> {
.collect()) .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> { fn cmd_wasm() -> Result<(), String> {
let set = wasm_set()?; let set = wasm_set()?;
let mut results = Vec::new(); if set.is_empty() {
for pkg in &set { return Err("no wasm-safe crates found in the workspace metadata".into());
}
let mut c = cargo(); let mut c = cargo();
c.args(["check", "--target", "wasm32-unknown-unknown", "-p", pkg]); c.args(["check", "--target", "wasm32-unknown-unknown", "--keep-going"]);
let ok = run(c, &format!("cargo check --target wasm32-unknown-unknown -p {pkg}")).is_ok(); for pkg in &set {
results.push((pkg.clone(), ok)); c.args(["-p", pkg]);
} }
println!("\nwasm32-unknown-unknown check:"); run(c, &format!("cargo check --target wasm32-unknown-unknown --keep-going -p {}", set.join(" -p ")))?;
for (p, ok) in &results { println!("\nwasm32-unknown-unknown check: ok ({} crates: {})", set.len(), set.join(", "));
println!(" {:<6} {p}", if *ok { "ok" } else { "FAIL" }); Ok(())
} }
let failed = results.iter().filter(|r| !r.1).count();
if failed == 0 { Ok(()) } else { Err(format!("{failed} crate(s) failed the wasm check")) } /// 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> { fn cmd_ci() -> Result<(), String> {
@@ -169,18 +202,12 @@ fn cmd_ci() -> Result<(), String> {
"clippy", "clippy",
Box::new(|| { Box::new(|| {
let mut c = cargo(); let mut c = cargo();
c.args(["clippy", "--workspace", "--all-targets", "--", "-D", "warnings"]); c.args(["clippy", "--workspace", "--all-targets", "--keep-going", "--", "-D", "warnings"]);
run(c, "cargo clippy --workspace --all-targets -- -D warnings") run(c, "cargo clippy --workspace --all-targets --keep-going -- -D warnings")
}),
),
(
"test",
Box::new(|| {
let mut c = cargo();
c.args(["test", "--workspace"]);
run(c, "cargo test --workspace")
}), }),
), ),
("test", Box::new(cmd_test)),
("doctest", Box::new(cmd_doctest)),
("assets", Box::new(|| assets::run(&root()))), ("assets", Box::new(|| assets::run(&root()))),
("layers", Box::new(cmd_layers)), ("layers", Box::new(cmd_layers)),
("wasm", Box::new(cmd_wasm)), ("wasm", Box::new(cmd_wasm)),