Files
cadcraft/crates/engine/tests/it/join_close.rs
T
aiongg 9cf115e808 CI: one test binary per crate, nextest, line tables only (#1989)
* Build: line tables only in dev, no debug info for dependencies

* Tests: one integration-test binary per crate

Each crate's tests/*.rs becomes a module of tests/it/main.rs. Files that
install process-wide state (the file io hooks, user aliases, SAVETIME and
SAVEFILEPATH, user linetype and pattern files, registered fonts) stay their
own test binaries.

* xtask ci: nextest, tests without examples, one wasm check call
2026-10-23 13:34:26 -04:00

97 lines
4.3 KiB
Rust

//! JOIN with a single arc or elliptical arc offers cLose, which makes it the full circle or
//! ellipse (#1175).
use cadcraft_engine::Session;
use cadcraft_engine::doc::{EntityKind, Handle};
use serde_json::{Value, json};
fn h(r: &Value) -> String {
r["handle"].as_str().or_else(|| r["handles"][0].as_str()).unwrap().to_string()
}
fn kind(s: &Session, h: &str) -> EntityKind {
s.doc().unwrap().entity(Handle::parse_hex(h).unwrap()).unwrap().kind.clone()
}
fn count(s: &Session) -> usize {
s.doc().unwrap().model.len()
}
#[test]
fn join_close_turns_a_single_arc_into_its_circle() {
let mut s = Session::new();
s.script("ARC C 50,50 80,50 50,80\nJOIN 80,50\n\n").unwrap();
let p = s.current_prompt().expect("JOIN asks what to do with the single arc");
assert!(p.keywords.iter().any(|k| k == "cLose"), "{p:?}");
s.cmdline("L").unwrap();
assert!(s.current_prompt().is_none());
assert_eq!(count(&s), 1);
match &s.doc().unwrap().model.iter().next().unwrap().kind {
EntityKind::Circle(c) => {
assert!(c.center.xy().near(cadcraft_engine::geom::Vec2::new(50.0, 50.0), 1e-9), "{c:?}");
assert!((c.radius - 30.0).abs() < 1e-9, "{c:?}");
}
k => panic!("not a circle: {k:?}"),
}
// Undo brings the arc back.
s.execute("undo", &json!({})).unwrap();
assert!(matches!(s.doc().unwrap().model.iter().next().unwrap().kind, EntityKind::Arc(_)));
}
#[test]
fn join_close_turns_a_single_elliptical_arc_into_its_ellipse() {
let mut s = Session::new();
let a = h(&s.execute("ellipse", &json!({ "center": [0, 0], "major": [4, 0], "ratio": 0.5, "start": 0, "end": 90 })).unwrap());
s.script("JOIN 4,0\n\n").unwrap();
let p = s.current_prompt().expect("JOIN asks what to do with the single elliptical arc");
assert_eq!(p.display(), "Select elliptical arcs to join to source or [cLose]:");
s.cmdline("cLose").unwrap();
assert_eq!(count(&s), 1);
match kind(&s, &a) {
EntityKind::Ellipse(e) => {
assert!(e.start.abs() < 1e-9 && (e.end - std::f64::consts::TAU).abs() < 1e-9, "{e:?}");
assert!((e.ratio - 0.5).abs() < 1e-12 && (e.major.x - 4.0).abs() < 1e-12, "{e:?}");
}
k => panic!("not an ellipse: {k:?}"),
}
}
#[test]
fn join_close_by_json_and_its_refusals() {
let mut s = Session::new();
let arc = h(&s.execute("arc", &json!({ "center": [50, 50], "radius": 30, "start": 0, "end": 90 })).unwrap());
let r = s.execute("join", &json!({ "handles": [arc], "close": true })).unwrap();
assert_eq!(h(&r), arc, "the arc keeps its handle");
match kind(&s, &arc) {
EntityKind::Circle(c) => assert!((c.radius - 30.0).abs() < 1e-9 && c.center.xy().near(cadcraft_engine::geom::Vec2::new(50.0, 50.0), 1e-9)),
k => panic!("not a circle: {k:?}"),
}
// Close is for one arc or elliptical arc: a line, a circle or two objects are refused,
// the drawing unchanged.
let line = h(&s.execute("line", &json!({ "points": [[0, 0], [10, 0]] })).unwrap());
let other = h(&s.execute("arc", &json!({ "center": [0, 0], "radius": 5, "start": 0, "end": 90 })).unwrap());
let before = format!("{:?}", s.doc().unwrap().model.iter().map(|e| e.kind.clone()).collect::<Vec<_>>());
for hs in [json!([line]), json!([arc]), json!([other, line])] {
assert!(s.execute("join", &json!({ "handles": hs, "close": true })).is_err(), "{hs}");
}
assert_eq!(format!("{:?}", s.doc().unwrap().model.iter().map(|e| e.kind.clone()).collect::<Vec<_>>()), before);
}
#[test]
fn join_close_prompt_takes_more_arcs_and_is_only_for_arcs() {
// Arcs picked at the prompt join to the source as they would from the selection.
let mut s = Session::new();
s.execute("arc", &json!({ "center": [0, 0], "radius": 5, "start": 0, "end": 90 })).unwrap();
s.execute("arc", &json!({ "center": [0, 0], "radius": 5, "start": 90, "end": 180 })).unwrap();
s.script("JOIN 5,0\n\n").unwrap();
s.cmdline("-5,0").unwrap();
s.cmdline("").unwrap();
assert!(s.current_prompt().is_none());
assert_eq!(count(&s), 1);
// A single line has nothing to close: the command ends with the usual refusal.
let mut s = Session::new();
s.script("LINE 0,0 10,0\n\nJOIN 5,0\n\n").unwrap();
assert!(s.current_prompt().is_none());
assert!(s.log.iter().any(|l| l == "Select at least two objects to join."), "{:?}", s.log);
}