use std::sync::Arc;
use crate::ExecutorContext;
use crate::ExecutorSettings;
use crate::engine::conn_mock::EngineConnection;
use crate::execution::ContextType;
use crate::execution::MockConfig;
use crate::front::Freedom;
use crate::front::ObjectKind;
use crate::frontend::api::ObjectId;
async fn run_with_freedom_analysis(kcl: &str) -> Vec<(ObjectId, Freedom)> {
let program = crate::Program::parse_no_errs(kcl).unwrap();
let exec_ctxt = ExecutorContext {
engine: Arc::new(Box::new(EngineConnection::new().unwrap())),
fs: Arc::new(crate::fs::FileManager::new()),
settings: ExecutorSettings::default(),
context_type: ContextType::Mock,
};
let mock_config = MockConfig {
freedom_analysis: true,
..Default::default()
};
let outcome = exec_ctxt.run_mock(&program, &mock_config).await.unwrap();
let mut point_freedoms = Vec::new();
for obj in &outcome.scene_objects {
if let ObjectKind::Segment {
segment: crate::front::Segment::Point(point),
} = &obj.kind
{
point_freedoms.push((obj.id, point.freedom));
}
}
point_freedoms.sort_by_key(|(id, _)| *id);
exec_ctxt.close().await;
point_freedoms
}
#[tokio::test(flavor = "multi_thread")]
async fn test_freedom_analysis_with_conflicts() {
let kcl = r#"
@settings(experimentalFeatures = allow)
sketch(on = YZ) {
line1 = line(start = [var 2mm, var 8mm], end = [var 5mm, var 7mm])
line1.start.at[0] == 2
line1.start.at[1] == 8
line1.end.at[0] == 5
line1.end.at[1] == 7
line2 = line(start = [var 2mm, var 1mm], end = [var -4.75mm, var -0.88mm])
line2.start.at[0] == 2
line2.start.at[1] == 1
line3 = line(start = [var -2.591mm, var -7.081mm], end = [var 1.331mm, var -3.979mm])
distance([line3.start, line3.end]) == 4mm
distance([line3.start, line3.end]) == 6mm
}
"#;
let point_freedoms = run_with_freedom_analysis(kcl).await;
let expected = vec![
(ObjectId(2), Freedom::Fixed),
(ObjectId(3), Freedom::Fixed),
(ObjectId(5), Freedom::Fixed),
(ObjectId(6), Freedom::Free),
(ObjectId(8), Freedom::Conflict),
(ObjectId(9), Freedom::Conflict),
];
assert_eq!(
point_freedoms, expected,
"Point freedoms should match expected values. Current behavior shows bugs with conflicts and reordered lines."
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_freedom_analysis_without_conflicts() {
let kcl = r#"
@settings(experimentalFeatures = allow)
sketch(on = YZ) {
line1 = line(start = [var 2mm, var 8mm], end = [var 5mm, var 7mm])
line1.start.at[0] == 2
line1.start.at[1] == 8
line1.end.at[0] == 5
line1.end.at[1] == 7
line2 = line(start = [var 2mm, var 1mm], end = [var -4.75mm, var -0.88mm])
line2.start.at[0] == 2
line2.start.at[1] == 1
line3 = line(start = [var -2.591mm, var -7.081mm], end = [var 1.331mm, var -3.979mm])
distance([line3.start, line3.end]) == 4mm
}
"#;
let point_freedoms = run_with_freedom_analysis(kcl).await;
let expected = vec![
(ObjectId(2), Freedom::Fixed),
(ObjectId(3), Freedom::Fixed),
(ObjectId(5), Freedom::Fixed),
(ObjectId(6), Freedom::Free),
(ObjectId(8), Freedom::Free),
(ObjectId(9), Freedom::Free),
];
assert_eq!(point_freedoms, expected, "Point freedoms should match expected values");
}
#[tokio::test(flavor = "multi_thread")]
async fn test_freedom_analysis_reordered_lines() {
let kcl = r#"
@settings(experimentalFeatures = allow)
sketch(on = YZ) {
line1 = line(start = [var 2mm, var 8mm], end = [var 5mm, var 7mm])
line1.start.at[0] == 2
line1.start.at[1] == 8
line1.end.at[0] == 5
line1.end.at[1] == 7
line3 = line(start = [var -2.591mm, var -7.081mm], end = [var 1.331mm, var -3.979mm])
distance([line3.start, line3.end]) == 4mm
distance([line3.start, line3.end]) == 6mm
line2 = line(start = [var 2mm, var 1mm], end = [var -4.75mm, var -0.88mm])
line2.start.at[0] == 2
line2.start.at[1] == 1
}
"#;
let point_freedoms = run_with_freedom_analysis(kcl).await;
let expected = vec![
(ObjectId(2), Freedom::Fixed),
(ObjectId(3), Freedom::Fixed),
(ObjectId(5), Freedom::Conflict),
(ObjectId(6), Freedom::Conflict),
(ObjectId(10), Freedom::Fixed),
(ObjectId(11), Freedom::Free),
];
assert_eq!(
point_freedoms, expected,
"Point freedoms should match expected values. Current behavior shows bug where line3.end is Free instead of Conflict."
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_freedom_analysis_with_zero_constraints() {
let kcl = r#"
@settings(experimentalFeatures = allow)
sketch(on = YZ) {
line1 = line(start = [var 1.32mm, var -1.93mm], end = [var 6.08mm, var 2.51mm])
line2 = line(start = [var -5.98mm, var 3.5mm], end = [var -8.52mm, var -1.59mm])
line3 = line(start = [var -6.66mm, var -3.03mm], end = [var 0.52mm, var -3.26mm])
}
"#;
let point_freedoms = run_with_freedom_analysis(kcl).await;
let expected = vec![
(ObjectId(2), Freedom::Free),
(ObjectId(3), Freedom::Free),
(ObjectId(5), Freedom::Free),
(ObjectId(6), Freedom::Free),
(ObjectId(8), Freedom::Free),
(ObjectId(9), Freedom::Free),
];
assert_eq!(
point_freedoms, expected,
"With 0 constraints, all points should be Free (underconstrained), not Fixed"
);
}