Fast PR CI: small hostile-test caps, heavy tests in a separate job (#43)

* test: hostile-params test runs under small session work caps

Session gains Limits, the defaults of mesh.tet.generate's max_steiner and
max_tets (production values unchanged; a parameter still overrides them;
file.open keeps them). The registry-wide hostile-params test runs on
sessions with small caps, so the needle-triangle tessellation it meshes is
refused at once instead of meshing to the 50k Steiner cap: the engine test
binary drops from ~350 s to ~20 s locally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* ci: move heavy tests to a heavy job on main, nightly and on demand

Four slow tests are #[ignore = "heavy: ..."] (every corpus recipe, the
cantilever convergence study, the explicit-vs-modal cantilever period, the
generic-predicate rational cross-check); fast representative tests of each
area stay in the PR gate. cargo xtask test --heavy runs every ignored test
(benchmarks included) and the doc tests; cargo xtask test and ci run the
rest without the doc tests. The new heavy CI job runs it on pushes to main,
nightly and on workflow_dispatch. Branch jobs restore main's rust cache and
no longer save their own. AGENTS.md documents the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

* test: the three slow LBM verification cases run in the heavy job

The Stokes-drag sphere array, the staircase-pipe convergence study and the
developing-channel case are heavy (21 s, 11 s, 6 s alone); plane
Poiseuille, Couette, Taylor-Green, reproducibility and the stability
limits stay in the PR gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm

---------

Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-10-13 21:21:49 -04:00
1 parent 7dc7a0e36d
commit 5e5a2ae251
13 files changed
+113 -18

No files matched your search

+21 -9
View File
@@ -30,15 +30,17 @@ commands:
licences [--check]
write THIRD-PARTY-LICENSES.txt (the licences of every shipped crate);
--check fails when it is out of date
test [CARGO ARGS] [-- TEST ARGS]
build the tests with the ci profile, then run every test binary at once
(and the doc tests alongside)
test [--heavy] [CARGO ARGS] [-- TEST ARGS]
build the tests with the ci profile, then run every test binary at once;
--heavy runs only the ignored tests instead (heavy verification and corpus
tests, and the benchmarks) and the doc tests alongside: the CI heavy job
corpus [--part NAME] [--no-write]
build every meshing test corpus part (tests/corpus, analytic shapes), tessellate
and mesh it, and write the results to docs/meshing/corpus.md (release build)
wasm cargo check every library crate for wasm32-unknown-unknown
ci fmt --check, assets, licences --check, layers, clippy -D warnings, then the tests
with the wasm check alongside (stops at the first failing step)
(not the heavy ones) with the wasm check alongside (stops at the first failing
step)
";
fn main() -> ExitCode {
@@ -51,8 +53,17 @@ fn main() -> ExitCode {
Some("app-icon") => app_icon::run(&root()),
Some("licences") => licences::run(&root(), rest.contains(&"--check")),
Some("test") => {
let (cargo_args, test_args) = rest.iter().position(|a| *a == "--").map_or((&rest[..], &[][..]), |i| (&rest[..i], &rest[i + 1..]));
test::run(cargo_args, test_args, vec![test::doc_tests(cargo_args, test_args)])
let (heavy, rest) = match rest.split_first() {
Some((&"--heavy", after)) => (true, after),
_ => (false, &rest[..]),
};
let (cargo_args, test_args) = rest.iter().position(|a| *a == "--").map_or((rest, &[][..]), |i| (&rest[..i], &rest[i + 1..]));
if heavy {
let ignored: Vec<&str> = ["--ignored"].into_iter().chain(test_args.iter().copied()).collect();
test::run(cargo_args, &ignored, vec![test::doc_tests(cargo_args, test_args)])
} else {
test::run(cargo_args, test_args, vec![])
}
}
Some("ci") => cmd_ci(),
Some("corpus") => {
@@ -159,8 +170,9 @@ fn wasm_check() -> Result<(Command, usize), String> {
Ok((c, set.len()))
}
/// The quick gates first, so they fail fast; the wasm check (and doc tests) run while the test
/// binaries do, which leaves cargo's build lock free.
/// The quick gates first, so they fail fast; the wasm check runs while the test binaries do,
/// which leaves cargo's build lock free. The heavy tests and the doc tests are the CI heavy job's
/// (`cargo xtask test --heavy`).
fn cmd_ci() -> Result<(), String> {
let mut c = cargo();
c.args(["fmt", "--all", "--", "--check"]);
@@ -172,7 +184,7 @@ fn cmd_ci() -> Result<(), String> {
c.args(["clippy", "--workspace", "--all-targets", "--profile", "ci", "--", "-D", "warnings"]);
run(c, "cargo clippy -D warnings")?;
let (wasm, n) = wasm_check()?;
test::run(&[], &[], vec![(format!("wasm32-unknown-unknown check ({n} crates)"), wasm), test::doc_tests(&[], &[])])?;
test::run(&[], &[], vec![(format!("wasm32-unknown-unknown check ({n} crates)"), wasm)])?;
eprintln!("ci: all gates passed");
Ok(())
}
+1 -1
View File
@@ -1,6 +1,6 @@
//! `cargo xtask test`: build the workspace's tests once, then run every test binary at the same
//! time. `cargo test` runs them one after another, so one slow binary held up all the others;
//! the doc tests and any other `side` jobs run alongside on their own thread.
//! `side` jobs (the wasm check, the doc tests) run alongside on their own thread.
use std::process::{Command, Stdio};
use std::sync::mpsc;