120 lines
4.9 KiB
Rust
120 lines
4.9 KiB
Rust
//! Regression tests: freshly drawn rectangles take every ordinary edit without being reported
|
|
//! over-constrained (dragging, dimensioning, pinning the centre, making them square).
|
|
|
|
use serde_json::{Value, json};
|
|
|
|
use crate::Session;
|
|
|
|
fn run(s: &mut Session, id: &str, p: Value) -> Value {
|
|
match s.execute(id, &p) {
|
|
Ok(v) => v,
|
|
Err(e) => panic!("{id} {p}: {e}"),
|
|
}
|
|
}
|
|
|
|
fn ids(v: &Value) -> Vec<String> {
|
|
v.as_array().map(|a| a.iter().filter_map(|x| x.as_str().map(str::to_string)).collect()).unwrap_or_default()
|
|
}
|
|
|
|
/// A new sketch with one rectangle of the given kind; returns its four side ids.
|
|
fn rect(kind: &str) -> (Session, Vec<String>) {
|
|
let mut s = Session::default();
|
|
run(&mut s, "sketch.create", json!({"plane": "XY"}));
|
|
let r = match kind {
|
|
"center" => run(&mut s, "sketch.rectangle.center", json!({"center": [3, 2], "corner": [13, 9]})),
|
|
"two" => run(&mut s, "sketch.rectangle.two_point", json!({"p0": [1, 1], "p1": [21, 15]})),
|
|
_ => run(&mut s, "sketch.rectangle.three_point", json!({"p0": [0, 0], "p1": [20, 5], "p2": [18, 15], "connect": false})),
|
|
};
|
|
let sides = ids(&r["curves"]);
|
|
assert_eq!(sides.len(), 4, "{r}");
|
|
(s, sides)
|
|
}
|
|
|
|
fn healthy(s: &mut Session, what: &str) -> i64 {
|
|
let si = run(s, "sketch.inspect", json!({}));
|
|
assert_ne!(si["status"], "conflict", "{what}: {si}");
|
|
assert!(si["status"].as_str().is_none_or(|t| !t.contains("over")), "{what}: {si}");
|
|
si["dof"].as_i64().unwrap_or(-1)
|
|
}
|
|
|
|
const KINDS: [&str; 3] = ["center", "two", "three"];
|
|
|
|
#[test]
|
|
fn fresh_rectangles_have_the_expected_freedom() {
|
|
for k in KINDS {
|
|
let (mut s, _) = rect(k);
|
|
let dof = healthy(&mut s, k);
|
|
// Centre/two-point: x, y, width, height. Three-point adds the rotation.
|
|
let want = if k == "three" { 5 } else { 4 };
|
|
assert_eq!(dof, want, "{k}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn rectangles_take_dimensions_without_conflict() {
|
|
for k in KINDS {
|
|
let (mut s, l) = rect(k);
|
|
run(&mut s, "sketch.dimension", json!({"entities": [l[0]], "value": 30}));
|
|
healthy(&mut s, &format!("{k} width"));
|
|
run(&mut s, "sketch.dimension", json!({"entities": [l[1]], "value": 12}));
|
|
let dof = healthy(&mut s, &format!("{k} width+height"));
|
|
assert_eq!(dof, if k == "three" { 3 } else { 2 }, "{k}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn rectangles_can_be_dragged_pinned_and_made_square() {
|
|
for k in KINDS {
|
|
// Drag a corner, then the middle of an edge.
|
|
let (mut s, l) = rect(k);
|
|
run(&mut s, "sketch.move_point", json!({"point": format!("{}.start", l[0]), "to": [-4, -3]}));
|
|
healthy(&mut s, &format!("{k} drag corner"));
|
|
run(&mut s, "sketch.move_point", json!({"point": format!("{}.end", l[2]), "to": [25, 22]}));
|
|
healthy(&mut s, &format!("{k} drag other corner"));
|
|
// Pin a corner to the origin.
|
|
run(&mut s, "sketch.constraint.coincident", json!({"a": format!("{}.start", l[0]), "b": "origin"}));
|
|
healthy(&mut s, &format!("{k} corner on origin"));
|
|
// Square: equal sides.
|
|
run(&mut s, "sketch.constraint.equal", json!({"a": l[0], "b": l[1]}));
|
|
healthy(&mut s, &format!("{k} square"));
|
|
run(&mut s, "sketch.dimension", json!({"entities": [l[0]], "value": 10}));
|
|
let dof = healthy(&mut s, &format!("{k} square sized"));
|
|
assert_eq!(dof, if k == "three" { 1 } else { 0 }, "{k}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn dragging_corners_never_collapses_a_side() {
|
|
for k in KINDS {
|
|
let (mut s, l) = rect(k);
|
|
for (i, to) in [[-4.0, -3.0], [25.0, 22.0], [30.0, -5.0], [-8.0, 18.0]].iter().enumerate() {
|
|
let pt = format!("{}.start", l[i % 4]);
|
|
run(&mut s, "sketch.move_point", json!({"point": pt, "to": to}));
|
|
let sk = s.model.state().sketch(s.active_sketch.unwrap()).unwrap().sketch.clone();
|
|
for c in &l {
|
|
let ci = sk.curve_index(c).unwrap();
|
|
let pi = sk.curves[ci].kind.point_ids();
|
|
let len = sk.point(pi[0]).unwrap().dist(sk.point(pi[1]).unwrap());
|
|
assert!(len > 0.5, "{k}: side {c} collapsed to {len} after dragging {pt}");
|
|
}
|
|
healthy(&mut s, &format!("{k} drag {i}"));
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn center_rectangle_draws_both_diagonals_like_fusion() {
|
|
let (mut s, _) = rect("center");
|
|
let si = run(&mut s, "sketch.inspect", json!({}));
|
|
let curves = si["curves"].as_array().cloned().unwrap_or_default();
|
|
// Four sides plus two construction diagonals; the other rectangles have no diagonals.
|
|
assert_eq!(curves.len(), 6, "{si}");
|
|
assert_eq!(curves.iter().filter(|c| c["construction"] == true).count(), 2, "{si}");
|
|
assert_eq!(healthy(&mut s, "center diagonals"), 4);
|
|
for k in ["two", "three"] {
|
|
let (mut s, _) = rect(k);
|
|
let si = run(&mut s, "sketch.inspect", json!({}));
|
|
assert_eq!(si["curves"].as_array().map(Vec::len), Some(4), "{k}: {si}");
|
|
}
|
|
}
|