Stepwise open checks every feature under the session's mode

OpenSteps::start evaluated under the default check mode, and finish kept those results under the session's mode: an each_step or off session opened in steps recomputed nothing. start now takes the mode (the app passes its session's), and a mode that changed before finish evaluates the design again under the new one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEu4Lr3moCCisdZShKz
This commit is contained in:
brett19
2026-11-01 00:29:11 -07:00
parent 012ed93b17
commit 55a46569f0
3 changed files with 62 additions and 8 deletions
+48 -3
View File
@@ -1968,7 +1968,8 @@ mod tests {
let mut whole = Session::default();
let opened = whole.execute("doc.open", &json!({"path": path, "read_only": true})).unwrap();
let mut steps = OpenSteps::start(&json!({"path": path, "read_only": true})).unwrap().expect("a design is opened in steps");
let mut steps =
OpenSteps::start(&json!({"path": path, "read_only": true}), Default::default()).unwrap().expect("a design is opened in steps");
assert_eq!(steps.path(), path);
assert_eq!(steps.progress(), (0, features - 1), "the saved marker is where evaluation stops");
let mut imports = 0;
@@ -2002,9 +2003,53 @@ mod tests {
// Other files, and STEP-only options, are opened by `doc.open`.
for p in [json!({"path": step}), json!({"path": "part.stl"}), json!({"path": path, "up": "y"}), json!({})] {
assert!(OpenSteps::start(&p).unwrap().is_none(), "{p}");
assert!(OpenSteps::start(&p, Default::default()).unwrap().is_none(), "{p}");
}
assert!(OpenSteps::start(&json!({"path": dir.join("missing.sc").to_string_lossy()}), Default::default()).is_err());
std::fs::remove_dir_all(dir).unwrap();
}
/// A design opened a feature at a time is evaluated under the session's check mode, as
/// `doc.open` is. Started under another mode than the session's (the mode changed meanwhile),
/// nothing evaluated under it stands: every feature is evaluated again. The bar's last round
/// is one the check refuses on solidcore (kernel case
/// fillets/1207-fillet-bar-top-edge-after-boss-arc-round-r0-5), so there the modes differ in
/// their errors too.
#[test]
fn a_design_opened_a_feature_at_a_time_is_checked_under_the_sessions_mode() {
use super::open_steps::OpenSteps;
use solidcraft_kernel::Validation;
let dir = std::env::temp_dir().join(format!("solidcraft-open-steps-mode-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("steps.sc").to_string_lossy().to_string();
let mut s = Session::default();
s.set_validation(Validation::Off);
s.execute("solid.box", &json!({"length": 100, "width": 30, "height": 10, "corner": [0, -15, 0], "body_name": "Arm"})).unwrap();
s.execute("solid.cylinder", &json!({"diameter": 40, "height": 20, "base": [0, 0, 0], "operation": "join", "targets": ["Arm"]})).unwrap();
s.execute("solid.fillet", &json!({"edges": [[16, 12, 10]], "radius": 2})).unwrap();
s.execute("solid.fillet", &json!({"edges": [[60, -15, 10]], "radius": 0.5})).unwrap();
s.execute("file.save", &json!({"path": path, "cache": false})).unwrap();
let errors = |s: &Session| s.model.results.iter().filter(|r| r.error.is_some()).count();
let open = |mode: Validation| {
let mut whole = Session::default();
whole.set_validation(mode);
whole.execute("doc.open", &json!({"path": path})).unwrap();
errors(&whole)
};
for mode in Validation::ALL {
for started in [mode, Validation::EachFeature, Validation::Off] {
let mut steps = OpenSteps::start(&json!({"path": path}), started).unwrap().expect("a design is opened in steps");
while steps.step() {}
let mut stepped = Session::default();
stepped.set_validation(mode);
steps.finish(&mut stepped).unwrap();
assert_eq!(stepped.validation(), mode);
assert_eq!(errors(&stepped), open(mode), "started under {started:?}, the session's mode {mode:?}");
let again = if started == mode { 0 } else { stepped.doc.features.len() };
assert_eq!(stepped.model.last_recomputed, again, "started under {started:?}, the session's mode {mode:?}");
assert!(stepped.model.is_current(&stepped.doc));
}
}
assert!(OpenSteps::start(&json!({"path": dir.join("missing.sc").to_string_lossy()})).is_err());
std::fs::remove_dir_all(dir).unwrap();
}
+12 -4
View File
@@ -36,7 +36,7 @@ impl OpenSteps {
/// Read the design file `doc.open` would open with parameters `p`, without evaluating it.
/// `None` when `p` names a file `doc.open` reads in another way (a CAD, mesh or drawing file,
/// or a point cloud), or asks for something only those take: run `doc.open` for it.
pub fn start(p: &Value) -> Result<Option<OpenSteps>> {
pub fn start(p: &Value, validation: solidcraft_kernel::Validation) -> Result<Option<OpenSteps>> {
let Some(path) = p.get("path").and_then(Value::as_str) else { return Ok(None) };
let other = solidcraft_io::is_step_path(path)
|| solidcraft_io::is_mesh_path(path)
@@ -58,7 +58,7 @@ impl OpenSteps {
let marker = doc.marker;
let stop = marker.unwrap_or(usize::MAX).min(doc.features.len());
// A model saved under another check mode holds that mode's results: evaluated again.
let adopted = Session::saved_model(&doc, saved.as_ref()).filter(|model| model.validation() == solidcraft_kernel::Validation::default());
let adopted = Session::saved_model(&doc, saved.as_ref()).filter(|model| model.validation() == validation);
let steps = OpenSteps {
path: path.to_string(),
read_only,
@@ -69,7 +69,7 @@ impl OpenSteps {
next: if adopted.is_some() { stop } else { 0 },
stop,
adopted: adopted.is_some(),
model: adopted.unwrap_or_default(),
model: adopted.unwrap_or_else(|| under(validation)),
};
Ok(Some(steps))
}
@@ -115,8 +115,9 @@ impl OpenSteps {
/// yet evaluated are evaluated now.
pub fn finish(mut self, s: &mut Session) -> Result<Value> {
Arc::make_mut(&mut self.doc).marker = self.marker;
// The results so far hold another mode's checks: evaluated again, as `Session::opened` would.
if self.model.validation() != s.validation() {
self.model.set_validation(s.validation());
self.model = under(s.validation());
self.adopted = false;
}
if !self.adopted {
@@ -128,3 +129,10 @@ impl OpenSteps {
opened_design(s, opened, &self.path, self.read_only, self.disk)
}
}
/// A model with no results yet, checking its features under `validation`.
fn under(validation: solidcraft_kernel::Validation) -> Model {
let mut model = Model::default();
model.set_validation(validation);
model
}
+2 -1
View File
@@ -766,7 +766,8 @@ fn step_opening(
let session = staged.session.as_mut()?;
let Some(opening) = staged.opening.as_mut() else {
let params = staged.params.clone();
match scoped(scope.clone(), || watch(progress.clone(), || solidcraft_engine::OpenSteps::start(&params))) {
let validation = session.validation();
match scoped(scope.clone(), || watch(progress.clone(), || solidcraft_engine::OpenSteps::start(&params, validation))) {
Ok(Some(opening)) => staged.opening = Some(opening),
// Options only `doc.open` takes: it opens the file at once.
Ok(None) => {