diff --git a/.config/nextest.toml b/.config/nextest.toml index 4cadd647..0a89d185 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -3,3 +3,8 @@ fail-fast = false [profile.ci.junit] path = "../../../test-results/junit.xml" + +[[profile.ci.overrides]] +filter = 'test(_a_kcl_) | test(=tests::snapshot_via_engine) | binary(=kcl_process)' +retries = 2 +slow-timeout = { period = "60s", terminate-after = 5 } diff --git a/src/tests.rs b/src/tests.rs index 3784d360..7035115d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -100,6 +100,7 @@ struct TestItem { args: Vec, stdin: Option, want_out: String, + stdout_check: Option, want_err: String, want_code: i32, current_directory: Option, @@ -114,6 +115,7 @@ impl TestItem { args, stdin: None, want_out: String::new(), + stdout_check: None, want_err: String::new(), want_code: 0, current_directory: None, @@ -137,6 +139,11 @@ impl TestItem { self } + fn check_stdout(mut self, check: fn(&str)) -> Self { + self.stdout_check = Some(check); + self + } + fn stderr_contains(mut self, want_err: impl Into) -> Self { self.want_err = want_err.into(); self @@ -373,9 +380,8 @@ async fn run_test_item_with_config(config: &mut TestConfig, item: TestItem) { "test '{}': unexpected exit code\nactual stdout: {stdout}\nactual stderr: {stderr}", item.name ); - assert_eq!( - stdout.is_empty(), - item.want_out.is_empty(), + assert!( + item.stdout_check.is_some() || stdout.is_empty() == item.want_out.is_empty(), "test '{}': stdout mismatch\nactual stdout: {stdout}\nexpected stdout to contain: {}", item.name, item.want_out @@ -395,6 +401,9 @@ async fn run_test_item_with_config(config: &mut TestConfig, item: TestItem) { item.want_out ); } + if let Some(check) = item.stdout_check { + check(&stdout); + } if !item.want_err.is_empty() { assert!( stderr.contains(&item.want_err), @@ -778,21 +787,6 @@ cli_tests! { .set_env("ENGINE_EXECUTION", "1") } - snapshot_a_kcl_file_with_a_project_toml_as_png(_ctx) => { - TestItem::new( - "snapshot a kcl file with a project.toml as png", - svec![ - "zoo", - "kcl", - "snapshot", - "tests/with-settings/gear.kcl", - "tests/with-settings/gear.png", - ], - ) - .setup(setup_authenticated) - .stdout_contains("Snapshot from engine saved to `tests/with-settings/gear.png`") - } - snapshot_a_kcl_file_with_a_nested_project_toml_as_png(_ctx) => { TestItem::new( "snapshot a kcl file with a nested project.toml as png", @@ -834,7 +828,7 @@ cli_tests! { "zoo", "kcl", "mass", - "tests/gear.kcl", + "tests/with-settings/cylinder.kcl", "--format=json", "--output-unit", "g", @@ -845,7 +839,17 @@ cli_tests! { ], ) .setup(setup_authenticated) - .stdout_contains("1267.973") + .check_stdout(|stdout| { + let mass: kittycad_modeling_cmds::output::Mass = + serde_json::from_str(stdout).expect("mass output must be JSON"); + let expected = 250.0 * std::f64::consts::PI * 453.59237 / 304.8_f64.powi(3); + assert_eq!(mass.output_unit, kittycad_modeling_cmds::units::UnitMass::Grams); + // Allow 0.2% error in the circular geometry's measured mass. + assert!( + (mass.mass - expected).abs() < expected * 0.002, + "mass: {mass:?}, expected about {expected} g" + ); + }) } get_the_mass_of_a_kcl_file_but_use_project_toml(_ctx) => { @@ -897,7 +901,7 @@ cli_tests! { "zoo", "kcl", "analyze", - "tests/gear.kcl", + "tests/with-settings/cylinder.kcl", "--volume-output-unit", "cm3", "--mass-output-unit", @@ -1027,6 +1031,33 @@ cli_tests! { } } +#[test_context(MainContext)] +#[tokio::test(flavor = "multi_thread", worker_threads = 3)] +#[serial_test::serial] +async fn snapshot_a_kcl_file_with_a_project_toml_as_png(ctx: &mut MainContext) { + let output_dir = tempfile::tempdir().expect("failed to create snapshot output dir"); + let output = output_dir.path().join("snapshot.png"); + + run_test_item( + ctx, + TestItem::new( + "snapshot a kcl file with a project.toml as png", + svec![ + "zoo", + "kcl", + "snapshot", + "tests/with-settings/cylinder.kcl", + output.display(), + ], + ) + .setup(setup_authenticated) + .stdout_contains(format!("Snapshot from engine saved to `{}`", output.display())), + ) + .await; + + assert!(output.is_file(), "snapshot file was not created"); +} + #[test_context(MainContext)] #[tokio::test(flavor = "multi_thread", worker_threads = 3)] #[serial_test::serial] diff --git a/src/update.rs b/src/update.rs index bf8b05a3..058c7a43 100644 --- a/src/update.rs +++ b/src/update.rs @@ -183,7 +183,7 @@ pub fn is_under_homebrew() -> Result { } /// Takes a version string and returns the URL to download the latest release. -fn get_exe_download_url(version: &str) -> String { +fn get_exe_download_url(version: &str, target: &str) -> String { // Make sure the version starts with a v. let version = if !version.starts_with('v') { format!("v{version}") @@ -191,7 +191,8 @@ fn get_exe_download_url(version: &str) -> String { version.to_string() }; - let target = crate::built_info::TARGET; + // Linux releases use musl so they also run on GNU systems. + let target = target.replace("-unknown-linux-gnu", "-unknown-linux-musl"); format!("https://github.com/KittyCAD/cli/releases/download/{version}/zoo-{target}") } @@ -201,14 +202,14 @@ pub async fn download_binary_to_temp_file(version: &str) -> Result { let temp_dir = std::env::temp_dir(); let temp_file = temp_dir.join("zoo"); - let url = get_exe_download_url(version); + let url = get_exe_download_url(version, crate::built_info::TARGET); // Get the contents of the binary. - let resp = reqwest::get(&url).await?; + let resp = reqwest::get(&url).await?.error_for_status()?; let bin_body = resp.bytes().await?; // Get the contents of the sha256sum. - let resp = reqwest::get(&format!("{url}.sha256")).await?; + let resp = reqwest::get(&format!("{url}.sha256")).await?.error_for_status()?; let sha256_body = resp.text().await?; let sha256_parts = sha256_body.split(' ').collect::>(); let sha256_hash = sha256_parts[0]; @@ -264,10 +265,6 @@ mod test { #[tokio::test] async fn test_download_binary_to_temp_file() { - if crate::update::is_ci() { - return; - } - let file = super::download_binary_to_temp_file("v0.2.42").await.unwrap(); let temp_base = std::env::temp_dir().to_str().unwrap().trim_end_matches('/').to_string(); @@ -276,18 +273,22 @@ mod test { #[test] fn test_get_exe_download_url() { - let url = super::get_exe_download_url("0.1.0"); - let target = crate::built_info::TARGET; - assert_eq!( - url, - format!("https://github.com/KittyCAD/cli/releases/download/v0.1.0/zoo-{target}") - ); - - let url = super::get_exe_download_url("v0.2.0"); - assert_eq!( - url, - format!("https://github.com/KittyCAD/cli/releases/download/v0.2.0/zoo-{target}") - ); + for (target, asset) in [ + ("x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"), + ("aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl"), + ("x86_64-unknown-linux-musl", "x86_64-unknown-linux-musl"), + ("aarch64-unknown-linux-musl", "aarch64-unknown-linux-musl"), + ("x86_64-apple-darwin", "x86_64-apple-darwin"), + ("aarch64-apple-darwin", "aarch64-apple-darwin"), + ("x86_64-pc-windows-gnu", "x86_64-pc-windows-gnu"), + ] { + for version in ["0.2.42", "v0.2.42"] { + assert_eq!( + super::get_exe_download_url(version, target), + format!("https://github.com/KittyCAD/cli/releases/download/v0.2.42/zoo-{asset}") + ); + } + } } #[tokio::test]