687 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			687 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
//! Cache testing framework.
 | 
						|
 | 
						|
use kcl_lib::{ExecError, ExecOutcome, bust_cache};
 | 
						|
#[cfg(feature = "artifact-graph")]
 | 
						|
use kcl_lib::{NodePathStep, exec::Operation};
 | 
						|
use kcmc::{ModelingCmd, each_cmd as mcmd};
 | 
						|
use kittycad_modeling_cmds as kcmc;
 | 
						|
use pretty_assertions::assert_eq;
 | 
						|
 | 
						|
#[derive(Debug)]
 | 
						|
struct Variation<'a> {
 | 
						|
    code: &'a str,
 | 
						|
    other_files: Vec<(std::path::PathBuf, std::string::String)>,
 | 
						|
    settings: &'a kcl_lib::ExecutorSettings,
 | 
						|
}
 | 
						|
 | 
						|
async fn cache_test(
 | 
						|
    test_name: &str,
 | 
						|
    variations: Vec<Variation<'_>>,
 | 
						|
) -> Vec<(String, image::DynamicImage, ExecOutcome)> {
 | 
						|
    let first = variations
 | 
						|
        .first()
 | 
						|
        .ok_or_else(|| anyhow::anyhow!("No variations provided for test '{}'", test_name))
 | 
						|
        .unwrap();
 | 
						|
 | 
						|
    let mut ctx = kcl_lib::ExecutorContext::new_with_client(first.settings.clone(), None, None)
 | 
						|
        .await
 | 
						|
        .unwrap();
 | 
						|
 | 
						|
    bust_cache().await;
 | 
						|
    let mut img_results = Vec::new();
 | 
						|
    for (index, variation) in variations.iter().enumerate() {
 | 
						|
        let program = kcl_lib::Program::parse_no_errs(variation.code).unwrap();
 | 
						|
 | 
						|
        // set the new settings.
 | 
						|
        ctx.settings = variation.settings.clone();
 | 
						|
 | 
						|
        if !variation.other_files.is_empty() {
 | 
						|
            let tmp_dir = std::env::temp_dir();
 | 
						|
            let tmp_dir = tmp_dir
 | 
						|
                .join(format!("kcl_test_{test_name}"))
 | 
						|
                .join(uuid::Uuid::new_v4().to_string());
 | 
						|
 | 
						|
            // Create a temporary file for each of the other files.
 | 
						|
            for (variant_path, variant_code) in &variation.other_files {
 | 
						|
                let tmp_file = tmp_dir.join(variant_path);
 | 
						|
                std::fs::create_dir_all(tmp_file.parent().unwrap()).unwrap();
 | 
						|
                std::fs::write(tmp_file, variant_code).unwrap();
 | 
						|
            }
 | 
						|
 | 
						|
            ctx.settings.project_directory = Some(kcl_lib::TypedPath(tmp_dir.clone()));
 | 
						|
        }
 | 
						|
 | 
						|
        let outcome = match ctx.run_with_caching(program).await {
 | 
						|
            Ok(outcome) => outcome,
 | 
						|
            Err(error) => {
 | 
						|
                let report = error.clone().into_miette_report_with_outputs(variation.code).unwrap();
 | 
						|
                let report = miette::Report::new(report);
 | 
						|
                panic!("{report:?}");
 | 
						|
            }
 | 
						|
        };
 | 
						|
 | 
						|
        let snapshot_png_bytes = ctx.prepare_snapshot().await.unwrap().contents.0;
 | 
						|
 | 
						|
        // Decode the snapshot, return it.
 | 
						|
        let img = image::ImageReader::new(std::io::Cursor::new(snapshot_png_bytes))
 | 
						|
            .with_guessed_format()
 | 
						|
            .map_err(|e| ExecError::BadPng(e.to_string()))
 | 
						|
            .and_then(|x| x.decode().map_err(|e| ExecError::BadPng(e.to_string())))
 | 
						|
            .unwrap();
 | 
						|
        // Save the snapshot.
 | 
						|
        let path = crate::assert_out(&format!("cache_{test_name}_{index}"), &img);
 | 
						|
 | 
						|
        img_results.push((path, img, outcome));
 | 
						|
    }
 | 
						|
 | 
						|
    ctx.close().await;
 | 
						|
 | 
						|
    img_results
 | 
						|
}
 | 
						|
 | 
						|
#[tokio::test(flavor = "multi_thread")]
 | 
						|
async fn kcl_test_cache_change_grid_visualizes_grid_off_to_on() {
 | 
						|
    let code = r#"part001 = startSketchOn(XY)
 | 
						|
  |> startProfile(at = [5.5229, 5.25217])
 | 
						|
  |> line(end = [10.50433, -1.19122])
 | 
						|
  |> line(end = [8.01362, -5.48731])
 | 
						|
  |> line(end = [-1.02877, -6.76825])
 | 
						|
  |> line(end = [-11.53311, 2.81559])
 | 
						|
  |> close()
 | 
						|
  |> extrude(length = 4)
 | 
						|
"#;
 | 
						|
 | 
						|
    let result = cache_test(
 | 
						|
        "change_grid_visualizes_grid_off_to_on",
 | 
						|
        vec![
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &kcl_lib::ExecutorSettings {
 | 
						|
                    show_grid: false,
 | 
						|
                    ..Default::default()
 | 
						|
                },
 | 
						|
            },
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &kcl_lib::ExecutorSettings {
 | 
						|
                    show_grid: true,
 | 
						|
                    ..Default::default()
 | 
						|
                },
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    )
 | 
						|
    .await;
 | 
						|
 | 
						|
    let first = result.first().unwrap();
 | 
						|
    let second = result.last().unwrap();
 | 
						|
 | 
						|
    assert!(first.1 != second.1);
 | 
						|
}
 | 
						|
 | 
						|
#[tokio::test(flavor = "multi_thread")]
 | 
						|
async fn kcl_test_cache_change_grid_visualizes_grid_on_to_off() {
 | 
						|
    let code = r#"part001 = startSketchOn(XY)
 | 
						|
  |> startProfile(at = [5.5229, 5.25217])
 | 
						|
  |> line(end = [10.50433, -1.19122])
 | 
						|
  |> line(end = [8.01362, -5.48731])
 | 
						|
  |> line(end = [-1.02877, -6.76825])
 | 
						|
  |> line(end = [-11.53311, 2.81559])
 | 
						|
  |> close()
 | 
						|
  |> extrude(length = 4)
 | 
						|
"#;
 | 
						|
 | 
						|
    let result = cache_test(
 | 
						|
        "change_grid_visualizes_grid_on_to_off",
 | 
						|
        vec![
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &kcl_lib::ExecutorSettings {
 | 
						|
                    show_grid: true,
 | 
						|
                    ..Default::default()
 | 
						|
                },
 | 
						|
            },
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &kcl_lib::ExecutorSettings {
 | 
						|
                    show_grid: false,
 | 
						|
                    ..Default::default()
 | 
						|
                },
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    )
 | 
						|
    .await;
 | 
						|
 | 
						|
    let first = result.first().unwrap();
 | 
						|
    let second = result.last().unwrap();
 | 
						|
 | 
						|
    assert!(first.1 != second.1);
 | 
						|
}
 | 
						|
 | 
						|
#[tokio::test(flavor = "multi_thread")]
 | 
						|
async fn kcl_test_cache_change_highlight_edges_changes_visual() {
 | 
						|
    let code = r#"part001 = startSketchOn(XY)
 | 
						|
  |> startProfile(at = [5.5229, 5.25217])
 | 
						|
  |> line(end = [10.50433, -1.19122])
 | 
						|
  |> line(end = [8.01362, -5.48731])
 | 
						|
  |> line(end = [-1.02877, -6.76825])
 | 
						|
  |> line(end = [-11.53311, 2.81559])
 | 
						|
  |> close()
 | 
						|
  |> extrude(length = 4)
 | 
						|
"#;
 | 
						|
 | 
						|
    let result = cache_test(
 | 
						|
        "change_highlight_edges_changes_visual",
 | 
						|
        vec![
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &kcl_lib::ExecutorSettings {
 | 
						|
                    highlight_edges: true,
 | 
						|
                    ..Default::default()
 | 
						|
                },
 | 
						|
            },
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &kcl_lib::ExecutorSettings {
 | 
						|
                    highlight_edges: false,
 | 
						|
                    ..Default::default()
 | 
						|
                },
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    )
 | 
						|
    .await;
 | 
						|
 | 
						|
    let first = result.first().unwrap();
 | 
						|
    let second = result.last().unwrap();
 | 
						|
 | 
						|
    assert!(first.1 != second.1);
 | 
						|
}
 | 
						|
 | 
						|
#[tokio::test(flavor = "multi_thread")]
 | 
						|
async fn kcl_test_cache_multi_file_same_code_dont_reexecute() {
 | 
						|
    let code = r#"import "toBeImported.kcl" as importedCube
 | 
						|
 | 
						|
importedCube
 | 
						|
 | 
						|
sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [-134.53, -56.17])
 | 
						|
  |> angledLine(angle = 0, length = 79.05, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 76.28)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $seg01)
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
 | 
						|
  |> close()
 | 
						|
extrude001 = extrude(profile001, length = 100)
 | 
						|
sketch003 = startSketchOn(extrude001, face = seg02)
 | 
						|
sketch002 = startSketchOn(extrude001, face = seg01)
 | 
						|
"#;
 | 
						|
 | 
						|
    let other_file = (
 | 
						|
        std::path::PathBuf::from("toBeImported.kcl"),
 | 
						|
        r#"sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [281.54, 305.81])
 | 
						|
  |> angledLine(angle = 0, length = 123.43, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 85.99)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
						|
  |> close()
 | 
						|
extrude(profile001, length = 100)"#
 | 
						|
            .to_string(),
 | 
						|
    );
 | 
						|
 | 
						|
    let result = cache_test(
 | 
						|
        "multi_file_same_code_dont_reexecute",
 | 
						|
        vec![
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![other_file.clone()],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![other_file],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    )
 | 
						|
    .await;
 | 
						|
 | 
						|
    let first = result.first().unwrap();
 | 
						|
    let last = result.last().unwrap();
 | 
						|
 | 
						|
    assert!(first.1 == last.1, "The images should be the same");
 | 
						|
    assert_eq!(first.2, last.2, "The outcomes should be the same");
 | 
						|
}
 | 
						|
 | 
						|
#[cfg(feature = "artifact-graph")]
 | 
						|
#[tokio::test(flavor = "multi_thread")]
 | 
						|
async fn kcl_test_cache_add_line_preserves_artifact_graph() {
 | 
						|
    let code = r#"sketch001 = startSketchOn(XY)
 | 
						|
profile001 = startProfile(sketch001, at = [5.5, 5.25])
 | 
						|
  |> line(end = [10.5, -1.19])
 | 
						|
  |> line(end = [8, -5.5])
 | 
						|
  |> line(end = [-1.02, -6.76])
 | 
						|
  |> line(end = [-11.5, 2.8])
 | 
						|
  |> close()
 | 
						|
plane001 = offsetPlane(XY, offset = 20)
 | 
						|
"#;
 | 
						|
    // Use a new statement; don't extend the prior pipeline.  This allows us to
 | 
						|
    // detect a prefix.
 | 
						|
    let code_with_extrude = code.to_owned()
 | 
						|
        + r#"
 | 
						|
profile002 = startProfile(plane001, at = [0, 0])
 | 
						|
  |> line(end = [0, 10])
 | 
						|
  |> line(end = [10, 0])
 | 
						|
  |> close()
 | 
						|
extrude001 = extrude(profile001, length = 4)
 | 
						|
"#;
 | 
						|
 | 
						|
    let result = cache_test(
 | 
						|
        "add_line_preserves_artifact_graph",
 | 
						|
        vec![
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
            Variation {
 | 
						|
                code: code_with_extrude.as_str(),
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    )
 | 
						|
    .await;
 | 
						|
 | 
						|
    let first = &result.first().unwrap().2;
 | 
						|
    let second = &result.last().unwrap().2;
 | 
						|
 | 
						|
    assert!(
 | 
						|
        first.artifact_graph.len() < second.artifact_graph.len(),
 | 
						|
        "Second should have all the artifacts of the first, plus more. first={:#?}, second={:#?}",
 | 
						|
        first.artifact_graph,
 | 
						|
        second.artifact_graph
 | 
						|
    );
 | 
						|
    assert!(
 | 
						|
        first.operations.len() < second.operations.len(),
 | 
						|
        "Second should have all the operations of the first, plus more. first={:?}, second={:?}",
 | 
						|
        first.operations.len(),
 | 
						|
        second.operations.len()
 | 
						|
    );
 | 
						|
    let Some(Operation::StdLibCall { name, .. }) = second.operations.last() else {
 | 
						|
        panic!("Last operation should be stdlib call extrude");
 | 
						|
    };
 | 
						|
    assert_eq!(name, "extrude");
 | 
						|
    // Make sure there are no duplicates.
 | 
						|
    assert_eq!(
 | 
						|
        second.operations.len(),
 | 
						|
        3,
 | 
						|
        "There should be exactly this many operations in the second run. {:#?}",
 | 
						|
        &second.operations
 | 
						|
    );
 | 
						|
    // Make sure we have NodePaths referring to the old code.
 | 
						|
    let graph = &second.artifact_graph;
 | 
						|
    assert!(!graph.is_empty());
 | 
						|
    for artifact in graph.values() {
 | 
						|
        assert!(!artifact.code_ref().map(|c| c.node_path.is_empty()).unwrap_or(false));
 | 
						|
        assert!(
 | 
						|
            !artifact
 | 
						|
                .face_code_ref()
 | 
						|
                // TODO: This fails, but it shouldn't.
 | 
						|
                // .map(|c| c.node_path.is_empty())
 | 
						|
                // Allowing the NodePath to be empty if the SourceRange is [0,
 | 
						|
                // 0] as a more lenient check.
 | 
						|
                .map(|c| !c.range.is_synthetic() && c.node_path.is_empty())
 | 
						|
                .unwrap_or(false),
 | 
						|
            "artifact={artifact:?}"
 | 
						|
        );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#[cfg(feature = "artifact-graph")]
 | 
						|
#[tokio::test(flavor = "multi_thread")]
 | 
						|
async fn kcl_test_cache_add_offset_plane_computes_node_path() {
 | 
						|
    let code = r#"sketch001 = startSketchOn(XY)
 | 
						|
profile001 = startProfile(sketch001, at = [0, 0])
 | 
						|
"#;
 | 
						|
    let code_with_more = code.to_owned()
 | 
						|
        + r#"plane001 = offsetPlane(XY, offset = 500)
 | 
						|
"#;
 | 
						|
 | 
						|
    let result = cache_test(
 | 
						|
        "add_offset_plane_preserves_artifact_commands",
 | 
						|
        vec![
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
            Variation {
 | 
						|
                code: code_with_more.as_str(),
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    )
 | 
						|
    .await;
 | 
						|
 | 
						|
    let second = &result.last().unwrap().2;
 | 
						|
 | 
						|
    let v = second.artifact_graph.values().collect::<Vec<_>>();
 | 
						|
    let path_step = &v[2].code_ref().unwrap().node_path.steps[0];
 | 
						|
    assert_eq!(*path_step, NodePathStep::ProgramBodyItem { index: 2 });
 | 
						|
}
 | 
						|
 | 
						|
#[tokio::test(flavor = "multi_thread")]
 | 
						|
async fn kcl_test_cache_empty_file_pop_cache_empty_file_planes_work() {
 | 
						|
    // Get the current working directory.
 | 
						|
    let code = "";
 | 
						|
 | 
						|
    let ctx = kcl_lib::ExecutorContext::new_with_default_client().await.unwrap();
 | 
						|
    let program = kcl_lib::Program::parse_no_errs(code).unwrap();
 | 
						|
    let outcome = ctx.run_with_caching(program).await.unwrap();
 | 
						|
 | 
						|
    // Ensure nothing is left in the batch
 | 
						|
    assert!(ctx.engine.batch().read().await.is_empty());
 | 
						|
    assert!(ctx.engine.batch_end().read().await.is_empty());
 | 
						|
 | 
						|
    // Ensure the planes work, and we can show or hide them.
 | 
						|
    // Hide/show the grid.
 | 
						|
    let default_planes = ctx.engine.get_default_planes().read().await.clone().unwrap();
 | 
						|
 | 
						|
    // Assure the outcome is the same.
 | 
						|
    assert_eq!(outcome.default_planes, Some(default_planes.clone()));
 | 
						|
 | 
						|
    ctx.engine
 | 
						|
        .send_modeling_cmd(
 | 
						|
            uuid::Uuid::new_v4(),
 | 
						|
            Default::default(),
 | 
						|
            &ModelingCmd::from(mcmd::ObjectVisible {
 | 
						|
                hidden: false,
 | 
						|
                object_id: default_planes.xy,
 | 
						|
            }),
 | 
						|
        )
 | 
						|
        .await
 | 
						|
        .unwrap();
 | 
						|
 | 
						|
    // Now simulate an engine pause/network disconnect.
 | 
						|
    // Raw dog clear the scene entirely.
 | 
						|
    ctx.engine
 | 
						|
        .send_modeling_cmd(
 | 
						|
            uuid::Uuid::new_v4(),
 | 
						|
            Default::default(),
 | 
						|
            &ModelingCmd::from(mcmd::SceneClearAll {}),
 | 
						|
        )
 | 
						|
        .await
 | 
						|
        .unwrap();
 | 
						|
 | 
						|
    // Bust the cache and reset the scene.
 | 
						|
    let outcome = ctx.bust_cache_and_reset_scene().await.unwrap();
 | 
						|
    // Get the default planes.
 | 
						|
    let default_planes = ctx.engine.get_default_planes().read().await.clone().unwrap();
 | 
						|
 | 
						|
    assert_eq!(outcome.default_planes, Some(default_planes.clone()));
 | 
						|
 | 
						|
    // Ensure we can show a plane.
 | 
						|
    ctx.engine
 | 
						|
        .send_modeling_cmd(
 | 
						|
            uuid::Uuid::new_v4(),
 | 
						|
            Default::default(),
 | 
						|
            &ModelingCmd::from(mcmd::ObjectVisible {
 | 
						|
                hidden: false,
 | 
						|
                object_id: default_planes.xz,
 | 
						|
            }),
 | 
						|
        )
 | 
						|
        .await
 | 
						|
        .unwrap();
 | 
						|
 | 
						|
    ctx.close().await;
 | 
						|
}
 | 
						|
 | 
						|
#[tokio::test(flavor = "multi_thread")]
 | 
						|
async fn kcl_test_cache_multi_file_after_empty_with_export() {
 | 
						|
    let code = r#"import importedCube from "toBeImported.kcl"
 | 
						|
 | 
						|
importedCube
 | 
						|
 | 
						|
sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [-134.53, -56.17])
 | 
						|
  |> angledLine(angle = 0, length = 79.05, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 76.28)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $seg01)
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
 | 
						|
  |> close()
 | 
						|
extrude001 = extrude(profile001, length = 100)
 | 
						|
sketch003 = startSketchOn(extrude001, face = seg02)
 | 
						|
sketch002 = startSketchOn(extrude001, face = seg01)
 | 
						|
"#;
 | 
						|
 | 
						|
    let other_file = (
 | 
						|
        std::path::PathBuf::from("toBeImported.kcl"),
 | 
						|
        r#"sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [281.54, 305.81])
 | 
						|
  |> angledLine(angle = 0, length = 123.43, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 85.99)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
						|
  |> close()
 | 
						|
export importedCube = extrude(profile001, length = 100)
 | 
						|
"#
 | 
						|
        .to_string(),
 | 
						|
    );
 | 
						|
 | 
						|
    let result = cache_test(
 | 
						|
        "multi_file_after_empty",
 | 
						|
        vec![
 | 
						|
            Variation {
 | 
						|
                code: "",
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![other_file],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    )
 | 
						|
    .await;
 | 
						|
 | 
						|
    result.first().unwrap();
 | 
						|
    result.last().unwrap();
 | 
						|
}
 | 
						|
 | 
						|
#[tokio::test(flavor = "multi_thread")]
 | 
						|
async fn kcl_test_cache_multi_file_after_empty_with_woo() {
 | 
						|
    let code = r#"import "toBeImported.kcl" as importedCube
 | 
						|
 | 
						|
importedCube
 | 
						|
 | 
						|
sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [-134.53, -56.17])
 | 
						|
  |> angledLine(angle = 0, length = 79.05, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 76.28)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $seg01)
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
 | 
						|
  |> close()
 | 
						|
extrude001 = extrude(profile001, length = 100)
 | 
						|
sketch003 = startSketchOn(extrude001, face = seg02)
 | 
						|
sketch002 = startSketchOn(extrude001, face = seg01)
 | 
						|
"#;
 | 
						|
 | 
						|
    let other_file = (
 | 
						|
        std::path::PathBuf::from("toBeImported.kcl"),
 | 
						|
        r#"sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [281.54, 305.81])
 | 
						|
  |> angledLine(angle = 0, length = 123.43, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 85.99)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
						|
  |> close()
 | 
						|
extrude(profile001, length = 100)
 | 
						|
"#
 | 
						|
        .to_string(),
 | 
						|
    );
 | 
						|
 | 
						|
    let result = cache_test(
 | 
						|
        "multi_file_after_empty",
 | 
						|
        vec![
 | 
						|
            Variation {
 | 
						|
                code: "",
 | 
						|
                other_files: vec![],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![other_file],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    )
 | 
						|
    .await;
 | 
						|
 | 
						|
    result.first().unwrap();
 | 
						|
    result.last().unwrap();
 | 
						|
}
 | 
						|
 | 
						|
#[cfg(feature = "artifact-graph")]
 | 
						|
#[tokio::test(flavor = "multi_thread")]
 | 
						|
async fn kcl_test_cache_multi_file_other_file_only_change() {
 | 
						|
    let code = r#"import "toBeImported.kcl" as importedCube
 | 
						|
 | 
						|
importedCube
 | 
						|
 | 
						|
sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [-134.53, -56.17])
 | 
						|
  |> angledLine(angle = 0, length = 79.05, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 76.28)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $seg01)
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
 | 
						|
  |> close()
 | 
						|
extrude001 = extrude(profile001, length = 100)
 | 
						|
sketch003 = startSketchOn(extrude001, face = seg02)
 | 
						|
sketch002 = startSketchOn(extrude001, face = seg01)
 | 
						|
"#;
 | 
						|
 | 
						|
    let other_file = (
 | 
						|
        std::path::PathBuf::from("toBeImported.kcl"),
 | 
						|
        r#"sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [281.54, 305.81])
 | 
						|
  |> angledLine(angle = 0, length = 123.43, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 85.99)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
						|
  |> close()
 | 
						|
extrude(profile001, length = 100)
 | 
						|
"#
 | 
						|
        .to_string(),
 | 
						|
    );
 | 
						|
 | 
						|
    let other_file2 = (
 | 
						|
        std::path::PathBuf::from("toBeImported.kcl"),
 | 
						|
        r#"sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [281.54, 305.81])
 | 
						|
  |> angledLine(angle = 0, length = 123.43, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 85.99)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
						|
  |> close()
 | 
						|
extrude(profile001, length = 100)
 | 
						|
    |> translate(z = 100)
 | 
						|
"#
 | 
						|
        .to_string(),
 | 
						|
    );
 | 
						|
 | 
						|
    let result = cache_test(
 | 
						|
        "multi_file_other_file_only_change",
 | 
						|
        vec![
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![other_file],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![other_file2],
 | 
						|
                settings: &Default::default(),
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    )
 | 
						|
    .await;
 | 
						|
 | 
						|
    let r1 = result.first().unwrap();
 | 
						|
    let r2 = result.last().unwrap();
 | 
						|
 | 
						|
    assert!(r1.1 != r2.1, "The images should be different");
 | 
						|
    // Make sure the outcomes are different.
 | 
						|
    assert!(
 | 
						|
        r1.2.artifact_graph != r2.2.artifact_graph,
 | 
						|
        "The outcomes artifact graphs should be different"
 | 
						|
    );
 | 
						|
}
 | 
						|
 | 
						|
#[tokio::test(flavor = "multi_thread")]
 | 
						|
async fn kcl_test_cache_multi_file_same_code_dont_reexecute_settings_only_change() {
 | 
						|
    let code = r#"import "toBeImported.kcl" as importedCube
 | 
						|
 | 
						|
importedCube
 | 
						|
 | 
						|
sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [-134.53, -56.17])
 | 
						|
  |> angledLine(angle = 0, length = 79.05, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 76.28)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $seg01)
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
 | 
						|
  |> close()
 | 
						|
extrude001 = extrude(profile001, length = 100)
 | 
						|
sketch003 = startSketchOn(extrude001, face = seg02)
 | 
						|
sketch002 = startSketchOn(extrude001, face = seg01)
 | 
						|
"#;
 | 
						|
 | 
						|
    let other_file = (
 | 
						|
        std::path::PathBuf::from("toBeImported.kcl"),
 | 
						|
        r#"sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [281.54, 305.81])
 | 
						|
  |> angledLine(angle = 0, length = 123.43, tag = $rectangleSegmentA001)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 85.99)
 | 
						|
  |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
 | 
						|
  |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
 | 
						|
  |> close()
 | 
						|
extrude(profile001, length = 100)"#
 | 
						|
            .to_string(),
 | 
						|
    );
 | 
						|
 | 
						|
    let result = cache_test(
 | 
						|
        "multi_file_same_code_dont_reexecute_settings_only_change",
 | 
						|
        vec![
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![other_file.clone()],
 | 
						|
                settings: &kcl_lib::ExecutorSettings {
 | 
						|
                    show_grid: false,
 | 
						|
                    ..Default::default()
 | 
						|
                },
 | 
						|
            },
 | 
						|
            Variation {
 | 
						|
                code,
 | 
						|
                other_files: vec![other_file],
 | 
						|
                settings: &kcl_lib::ExecutorSettings {
 | 
						|
                    show_grid: true,
 | 
						|
                    ..Default::default()
 | 
						|
                },
 | 
						|
            },
 | 
						|
        ],
 | 
						|
    )
 | 
						|
    .await;
 | 
						|
 | 
						|
    let first = result.first().unwrap();
 | 
						|
    let last = result.last().unwrap();
 | 
						|
 | 
						|
    assert!(first.1 != last.1, "The images should be different for the grid");
 | 
						|
    assert_eq!(first.2, last.2, "The outcomes should be the same");
 | 
						|
}
 |