Skip to content
Merged
5 changes: 5 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
73 changes: 52 additions & 21 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ struct TestItem {
args: Vec<String>,
stdin: Option<String>,
want_out: String,
stdout_check: Option<fn(&str)>,
want_err: String,
want_code: i32,
current_directory: Option<PathBuf>,
Expand All @@ -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,
Expand All @@ -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<String>) -> Self {
self.want_err = want_err.into();
self
Expand Down Expand Up @@ -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
Expand All @@ -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),
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -834,7 +828,7 @@ cli_tests! {
"zoo",
"kcl",
"mass",
"tests/gear.kcl",
"tests/with-settings/cylinder.kcl",
"--format=json",
"--output-unit",
"g",
Expand All @@ -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) => {
Expand Down Expand Up @@ -897,7 +901,7 @@ cli_tests! {
"zoo",
"kcl",
"analyze",
"tests/gear.kcl",
"tests/with-settings/cylinder.kcl",
"--volume-output-unit",
"cm3",
"--mass-output-unit",
Expand Down Expand Up @@ -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]
Expand Down
43 changes: 22 additions & 21 deletions src/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,15 +183,16 @@ pub fn is_under_homebrew() -> Result<bool> {
}

/// 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}")
} else {
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}")
}

Expand All @@ -201,14 +202,14 @@ pub async fn download_binary_to_temp_file(version: &str) -> Result<String> {
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::<Vec<&str>>();
let sha256_hash = sha256_parts[0];
Expand Down Expand Up @@ -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();
Expand All @@ -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]
Expand Down
Loading