test: seeded goldens for the robustness seeds (#826)

tests/robustness/goldens.json records what each of seeds 0-95 builds: its steps, how it ended (ok, or the kind and the start of the error it was refused with) and, for the design it left, the bodies' total volume and area (within a relative 1e-6) and their face, edge and shell counts. tests::robustness_goldens fails on any change; ROBUSTNESS_GOLDENS_BLESS="why" records one, with the reason and the seeds it moved. Pull requests that touch crates/kernel run every 8th seed; pushes to main and the nightly run every seed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-13 16:23:27 -04:00
1 parent 200a83a629
commit 534393ed5a
9 files changed
+338 -5

No files matched your search

+43 -2
View File
@@ -289,6 +289,47 @@ pub fn run(seed: u64, max_steps: usize) -> Run {
/// [`run`], telling `on_step` about each step before it runs (to see where a hang happened).
pub fn run_with(seed: u64, max_steps: usize, on_step: &mut dyn FnMut(&Step)) -> Run {
run_session(seed, max_steps, on_step).0
}
/// What a seed builds, as recorded in the robustness goldens (`tests/robustness/goldens.json`):
/// the steps, how it ended (with the start of the message when it did not end ok) and, for the
/// design it left, its bodies' total volume and area and their face, edge and shell counts.
pub fn golden(seed: u64, max_steps: usize) -> Value {
let (run, s) = run_session(seed, max_steps, &mut |_| {});
let st = s.model.state();
let (mut volume, mut area, mut faces, mut edges, mut shells) = (0.0, 0.0, 0, 0, 0);
let mut measured = true;
for b in &st.bodies {
match solidcraft_kernel::measure(&b.body) {
Ok(m) => {
volume += m.volume;
area += m.area;
faces += m.faces;
edges += m.edges;
shells += m.shells;
}
Err(_) => measured = false,
}
}
let round = |x: f64| (x * 1e6).round() / 1e6;
json!({
"steps": run.steps.iter().map(|st| st.kind).collect::<Vec<_>>(),
"outcome": run.outcome.kind,
"error": (run.outcome.kind != "ok").then(|| gist(&run.outcome.detail)),
"unsupported": run.unsupported.len(),
"rejected": run.rejected,
"bodies": st.bodies.len(),
"volume": measured.then(|| round(volume)),
"area": measured.then(|| round(area)),
"faces": faces,
"edges": edges,
"shells": shells,
})
}
/// [`run_with`], and the session the run left.
fn run_session(seed: u64, max_steps: usize, on_step: &mut dyn FnMut(&Step)) -> (Run, Session) {
let mut r = Rng::new(seed);
let mut s = Session::default();
let mut run = Run {
@@ -333,13 +374,13 @@ pub fn run_with(seed: u64, max_steps: usize, on_step: &mut dyn FnMut(&Step)) ->
run.steps.push(st);
if out.kind != "ok" {
run.outcome = out;
return run;
return (run, s);
}
if matches!(kind, "join box" | "cut box" | "join cylinder" | "cut cylinder" | "cut sphere" | "hole") {
last_feature = s.doc.features.last().map(|f| f.name.clone());
}
}
run
(run, s)
}
/// Run fixed steps (a recorded or minimised design): the index and outcome of the first step