Test server works for one request at a time.

TODO: Concurrency control
This commit is contained in:
Adam Chalmers
2024-06-06 17:42:48 -05:00
parent c66f851a3f
commit c12b5b67ee
9 changed files with 364 additions and 140 deletions

View File

@ -59,11 +59,19 @@ jobs:
- uses: taiki-e/install-action@nextest
- name: Rust Cache
uses: Swatinem/rust-cache@v2.6.1
- name: cargo test
- name: Compile tests
run: |-
cd "${{ matrix.dir }}"
c nextest archive --archive-file tests.tar.zst
- name: Start test KCL server
run: |-
cd "${{ matrix.dir }}"
c build --quiet --bin kcl-test-server --workspace && ./target/debug/kcl-test-server
- name: Run tests
shell: bash
run: |-
cd "${{ matrix.dir }}"
cargo llvm-cov nextest --all --lcov --output-path lcov.info --test-threads=1 --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
cargo llvm-cov nextest --all --lcov --output-path lcov.info --test-threads=1 --no-fail-fast -P ci --archive-file t.tar.zst 2>&1 | tee /tmp/github-actions.log
env:
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
RUST_MIN_STACK: 10485760000

View File

@ -1,3 +1,8 @@
# experimental = ["setup-scripts"]
# [script.test-server]
# command = "just start-test-server"
# Each test can have at most 4 threads, but if its name contains "serial_test_", then it
# also requires 4 threads.
# This means such tests run one at a time, with 4 threads.
@ -14,12 +19,20 @@ slow-timeout = { period = "50s", terminate-after = 5 }
[[profile.default.overrides]]
filter = "test(serial_test_)"
test-group = "serial-integration"
threads-required = 2
threads-required = 4
# [[profile.default.scripts]]
# filter = 'test(serial_test_)'
# setup = 'test-server'
[[profile.ci.overrides]]
filter = "test(serial_test_)"
test-group = "serial-integration"
threads-required = 2
threads-required = 4
# [[profile.default.scripts]]
# filter = 'test(serial_test_)'
# setup = 'test-server'
[[profile.default.overrides]]
filter = "test(parser::parser_impl::snapshot_tests)"

View File

@ -1434,6 +1434,18 @@ dependencies = [
"syn 2.0.66",
]
[[package]]
name = "kcl-test-server"
version = "0.1.0"
dependencies = [
"anyhow",
"hyper",
"kcl-lib",
"serde",
"serde_json",
"tokio",
]
[[package]]
name = "kittycad"
version = "0.3.5"
@ -2492,9 +2504,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.116"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [
"indexmap 2.2.5",
"itoa",

View File

@ -17,7 +17,7 @@ kcl-lib = { path = "kcl" }
kittycad.workspace = true
serde_json = "1.0.116"
tokio = { version = "1.38.0", features = ["sync"] }
toml = "0.8.14"
toml = "0.8.13"
uuid = { version = "1.8.0", features = ["v4", "js", "serde"] }
wasm-bindgen = "0.2.91"
wasm-bindgen-futures = "0.4.42"
@ -65,6 +65,7 @@ members = [
"derive-docs",
"kcl",
"kcl-macros",
"kcl-test-server",
]
[workspace.dependencies]

2
src/wasm-lib/justfile Normal file
View File

@ -0,0 +1,2 @@
start-test-server:
cargo build --quiet --bin kcl-test-server --workspace && ./target/debug/kcl-test-server

View File

@ -0,0 +1,12 @@
[package]
name = "kcl-test-server"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.86"
hyper = { version = "0.14.29", features = ["server"] }
kcl-lib = { path = "../kcl" }
serde = { version = "1.0.203", features = ["derive"] }
serde_json = "1.0.117"
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }

View File

@ -0,0 +1,112 @@
//! Executes KCL programs.
//! The server reuses the same engine session for each KCL program it receives.
use std::net::SocketAddr;
use hyper::header::CONTENT_TYPE;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Error, Response, Server};
use kcl_lib::executor::ExecutorContext;
use kcl_lib::settings::types::UnitLength;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Parse the CLI arguments.
let mut args: Vec<_> = std::env::args().collect();
args.reverse();
let _process_name = args.pop().unwrap();
let listen_on = args.pop().unwrap_or_else(|| "0.0.0.0:3333".to_owned()).parse()?;
// Run the actual server.
start(listen_on).await
}
#[derive(serde::Deserialize)]
pub struct RequestBody {
kcl_program: String,
#[serde(default)]
test_name: String,
}
pub async fn start(listen_on: SocketAddr) -> anyhow::Result<()> {
let state = ExecutorContext::new_for_unit_test(UnitLength::Mm).await?;
// In hyper, a `MakeService` is basically your server.
// It makes a `Service` for each connection, which manages the connection.
let make_service = make_service_fn(
// This closure is run for each connection.
move |_conn_info| {
// eprintln!("Connected to a client");
let state2 = state.clone();
async move {
// This is the `Service` which handles the connection.
// `service_fn` converts a function which returns a Response
// into a `Service`.
Ok::<_, Error>(service_fn(move |req| {
// eprintln!("Received a request");
let state3 = state2.clone();
// TODO: Don't let multiple requests through at once.
async move {
let whole_body = hyper::body::to_bytes(req.into_body()).await?;
Ok::<_, Error>(snapshot_endpoint(whole_body.into(), state3).await)
}
}))
}
},
);
let server = Server::bind(&listen_on).serve(make_service);
println!("Listening on {listen_on}");
println!("PID is {}", std::process::id());
if let Err(e) = server.await {
eprintln!("Server error: {e}");
return Err(e.into());
}
Ok(())
}
/// Execute a KCL program, then respond with a PNG snapshot.
/// KCL errors (from engine or the executor) respond with HTTP Bad Gateway.
/// Malformed requests are HTTP Bad Request.
/// Successful requests contain a PNG as the body.
async fn snapshot_endpoint(body: Vec<u8>, state3: ExecutorContext) -> Response<Body> {
let body = match serde_json::from_slice::<RequestBody>(&body) {
Ok(bd) => bd,
Err(e) => return bad_request(format!("Invalid request JSON: {e}")),
};
let RequestBody { kcl_program, test_name } = body;
eprintln!("Executing {test_name}");
let parser = match kcl_lib::token::lexer(&kcl_program) {
Ok(ts) => kcl_lib::parser::Parser::new(ts),
Err(e) => return bad_request(format!("tokenization error: {e}")),
};
let program = match parser.ast() {
Ok(pr) => pr,
Err(e) => return bad_request(format!("Parse error: {e}")),
};
if let Err(e) = state3.reset_scene().await {
return kcl_err(e);
}
let snapshot = match state3.execute_and_prepare_snapshot(program).await {
Ok(sn) => sn,
Err(e) => return kcl_err(e),
};
eprintln!("Serving response");
let png_bytes = snapshot.contents.0;
let mut resp = Response::new(Body::from(png_bytes));
resp.headers_mut().insert(CONTENT_TYPE, "image/png".parse().unwrap());
resp
}
fn bad_request(msg: String) -> Response<Body> {
let mut resp = Response::new(Body::from(msg));
*resp.status_mut() = hyper::StatusCode::BAD_REQUEST;
resp
}
fn bad_gateway(msg: String) -> Response<Body> {
let mut resp = Response::new(Body::from(msg));
*resp.status_mut() = hyper::StatusCode::BAD_GATEWAY;
resp
}
fn kcl_err(err: anyhow::Error) -> Response<Body> {
bad_gateway(format!("{err}"))
}

View File

@ -15,6 +15,7 @@ use crate::{
engine::EngineManager,
errors::{KclError, KclErrorDetails},
fs::FileManager,
settings::types::UnitLength,
std::{FunctionKind, StdLib},
};
@ -992,7 +993,7 @@ pub struct ExecutorContext {
#[derive(Debug, Clone)]
pub struct ExecutorSettings {
/// The unit to use in modeling dimensions.
pub units: crate::settings::types::UnitLength,
pub units: UnitLength,
/// Highlight edges of 3D objects?
pub highlight_edges: bool,
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
@ -1083,6 +1084,56 @@ impl ExecutorContext {
})
}
/// For executing unit tests.
pub async fn new_for_unit_test(units: UnitLength) -> Result<Self> {
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
let http_client = reqwest::Client::builder()
.user_agent(user_agent)
// For file conversions we need this to be long.
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60));
let ws_client = reqwest::Client::builder()
.user_agent(user_agent)
// For file conversions we need this to be long.
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60))
.connection_verbose(true)
.tcp_keepalive(std::time::Duration::from_secs(600))
.http1_only();
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
// Create the client.
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
// Set a local engine address if it's set.
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
client.set_base_url(addr);
}
let ctx = ExecutorContext::new(
&client,
ExecutorSettings {
units,
highlight_edges: true,
enable_ssao: false,
},
)
.await?;
Ok(ctx)
}
/// Clear everything in the scene.
pub async fn reset_scene(&self) -> Result<()> {
self.engine
.send_modeling_cmd(
uuid::Uuid::new_v4().into(),
SourceRange::default(),
kittycad::types::ModelingCmd::SceneClearAll {},
)
.await?;
Ok(())
}
/// Perform the execution of a program.
/// You can optionally pass in some initialization memory.
/// Kurt uses this for partial execution.
@ -1309,7 +1360,7 @@ impl ExecutorContext {
}
/// Update the units for the executor.
pub fn update_units(&mut self, units: crate::settings::types::UnitLength) {
pub fn update_units(&mut self, units: UnitLength) {
self.settings.units = units;
}

View File

@ -1,58 +1,39 @@
use std::io::Cursor;
use anyhow::Result;
use kcl_lib::{
executor::{ExecutorContext, ExecutorSettings},
settings::types::UnitLength,
};
use image::io::Reader as ImageReader;
use kcl_lib::{executor::ExecutorContext, settings::types::UnitLength};
use kittycad::types::TakeSnapshot;
// mod server;
async fn new_context(units: UnitLength) -> Result<ExecutorContext> {
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
let http_client = reqwest::Client::builder()
.user_agent(user_agent)
// For file conversions we need this to be long.
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60));
let ws_client = reqwest::Client::builder()
.user_agent(user_agent)
// For file conversions we need this to be long.
.timeout(std::time::Duration::from_secs(600))
.connect_timeout(std::time::Duration::from_secs(60))
.connection_verbose(true)
.tcp_keepalive(std::time::Duration::from_secs(600))
.http1_only();
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
// Create the client.
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
// Set a local engine address if it's set.
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
client.set_base_url(addr);
}
let ctx = ExecutorContext::new(
&client,
ExecutorSettings {
units,
highlight_edges: true,
enable_ssao: false,
},
)
.await?;
Ok(ctx)
macro_rules! test_name {
() => {{
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
let name = type_name_of(f);
name.strip_suffix("::f")
.unwrap()
.strip_suffix("::{{closure}}")
.unwrap()
.strip_prefix("executor::serial_test_")
.unwrap()
}};
}
/// Executes a kcl program and takes a snapshot of the result.
/// This returns the bytes of the snapshot.
async fn execute_and_snapshot(code: &str, units: UnitLength) -> Result<image::DynamicImage> {
let ctx = new_context(units).await?;
let ctx = ExecutorContext::new_for_unit_test(units).await?;
let tokens = kcl_lib::token::lexer(code)?;
let parser = kcl_lib::parser::Parser::new(tokens);
let program = parser.ast()?;
let snapshot = ctx.execute_and_prepare_snapshot(program).await?;
to_disk(snapshot)
}
fn to_disk(snapshot: TakeSnapshot) -> Result<image::DynamicImage> {
// Create a temporary file to write the output to.
let output_file = std::env::temp_dir().join(format!("kcl_output_{}.png", uuid::Uuid::new_v4()));
// Save the snapshot locally, to that temporary file.
@ -81,28 +62,28 @@ const part002 = startSketchOn(part001, "here")
|> extrude(5, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_riddle_small() {
let code = include_str!("inputs/riddle_small.kcl");
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/riddle_small.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_lego() {
let code = include_str!("inputs/lego.kcl");
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/lego.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_pipe_as_arg() {
let code = include_str!("inputs/pipe_as_arg.kcl");
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/pipe_as_arg.png", &result, 0.999);
}
@ -137,14 +118,14 @@ const part002 = startSketchOn(part001, "start")
|> extrude(5, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_start.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_mike_stress_lines() {
let code = include_str!("inputs/mike_stress_test.kcl");
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/mike_stress_test.png", &result, 0.999);
}
@ -172,7 +153,7 @@ const part002 = startSketchOn(part001, "END")
|> extrude(5, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_end.png", &result, 0.999);
}
@ -200,7 +181,7 @@ const part002 = startSketchOn(part001, "END")
|> extrude(-5, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/sketch_on_face_end_negative_extrude.png",
&result,
@ -220,7 +201,7 @@ async fn serial_test_fillet_duplicate_tags() {
|> fillet({radius: 0.5, tags: ["thing", "thing"]}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
@ -240,7 +221,7 @@ async fn serial_test_basic_fillet_cube_start() {
|> fillet({radius: 2, tags: ["thing", "thing2"]}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/basic_fillet_cube_start.png", &result, 0.999);
}
@ -257,7 +238,7 @@ async fn serial_test_basic_fillet_cube_end() {
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/basic_fillet_cube_end.png", &result, 0.999);
}
@ -274,7 +255,7 @@ async fn serial_test_basic_fillet_cube_close_opposite() {
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/basic_fillet_cube_close_opposite.png",
&result,
@ -294,7 +275,7 @@ async fn serial_test_basic_fillet_cube_next_adjacent() {
|> fillet({radius: 2, tags: [getNextAdjacentEdge("thing3", %)]}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/basic_fillet_cube_next_adjacent.png",
&result,
@ -314,7 +295,7 @@ async fn serial_test_basic_fillet_cube_previous_adjacent() {
|> fillet({radius: 2, tags: [getPreviousAdjacentEdge("thing3", %)]}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/basic_fillet_cube_previous_adjacent.png",
&result,
@ -339,7 +320,7 @@ async fn serial_test_execute_with_function_sketch() {
const fnBox = box(3, 6, 10)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/function_sketch.png", &result, 0.999);
}
@ -359,7 +340,7 @@ async fn serial_test_execute_with_function_sketch_with_position() {
const thing = box([0,0], 3, 6, 10)"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/function_sketch_with_position.png",
&result,
@ -380,7 +361,7 @@ async fn serial_test_execute_with_angled_line() {
|> extrude(4, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/angled_line.png", &result, 0.999);
}
@ -406,7 +387,7 @@ const bracket = startSketchOn('XY')
|> extrude(width, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/parametric.png", &result, 0.999);
}
@ -440,7 +421,7 @@ const bracket = startSketchAt([0, 0])
|> extrude(width, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/parametric_with_tan_arc.png", &result, 0.999);
}
@ -455,7 +436,7 @@ async fn serial_test_execute_engine_error_return() {
|> extrude(4, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
@ -468,7 +449,7 @@ async fn serial_test_execute_i_shape() {
// This is some code from lee that starts a pipe expression with a variable.
let code = include_str!("inputs/i_shape.kcl");
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/i_shape.png", &result, 0.999);
}
@ -477,7 +458,7 @@ async fn serial_test_execute_i_shape() {
async fn serial_test_execute_pipes_on_pipes() {
let code = include_str!("inputs/pipes_on_pipes.kcl");
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/pipes_on_pipes.png", &result, 0.999);
}
@ -485,7 +466,7 @@ async fn serial_test_execute_pipes_on_pipes() {
async fn serial_test_execute_cylinder() {
let code = include_str!("inputs/cylinder.kcl");
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/cylinder.png", &result, 0.999);
}
@ -493,7 +474,7 @@ async fn serial_test_execute_cylinder() {
async fn serial_test_execute_kittycad_svg() {
let code = include_str!("inputs/kittycad_svg.kcl");
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/kittycad_svg.png", &result, 0.999);
}
@ -518,7 +499,7 @@ const pt1 = b1.value[0]
const pt2 = b2.value[0]
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/member_expression_sketch_group.png",
&result,
@ -534,7 +515,7 @@ async fn serial_test_helix_defaults() {
|> helix({revolutions: 16, angle_start: 0}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/helix_defaults.png", &result, 1.0);
}
@ -546,7 +527,7 @@ async fn serial_test_helix_defaults_negative_extrude() {
|> helix({revolutions: 16, angle_start: 0}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/helix_defaults_negative_extrude.png",
&result,
@ -562,7 +543,7 @@ async fn serial_test_helix_ccw() {
|> helix({revolutions: 16, angle_start: 0, ccw: true}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/helix_ccw.png", &result, 1.0);
}
@ -574,7 +555,7 @@ async fn serial_test_helix_with_length() {
|> helix({revolutions: 16, angle_start: 0, length: 3}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/helix_with_length.png", &result, 1.0);
}
@ -589,7 +570,7 @@ async fn serial_test_dimensions_match() {
|> extrude(10, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/dimensions_match.png", &result, 1.0);
}
@ -606,7 +587,7 @@ const body = startSketchOn('XY')
|> extrude(height, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/close_arc.png", &result, 0.999);
}
@ -632,7 +613,7 @@ box(10, 23, 8)
let thing = box(-12, -15, 10)
box(-20, -5, 10)"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/negative_args.png", &result, 0.999);
}
@ -645,7 +626,7 @@ async fn serial_test_basic_tangential_arc() {
|> extrude(10, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc.png", &result, 0.999);
}
@ -658,7 +639,7 @@ async fn serial_test_basic_tangential_arc_with_point() {
|> extrude(10, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc_with_point.png", &result, 0.999);
}
@ -671,7 +652,7 @@ async fn serial_test_basic_tangential_arc_to() {
|> extrude(10, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc_to.png", &result, 0.999);
}
@ -698,7 +679,7 @@ box(30, 43, 18, '-xy')
let thing = box(-12, -15, 10, 'yz')
box(-20, -5, 10, 'xy')"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/different_planes_same_drawing.png",
&result,
@ -760,7 +741,7 @@ const part004 = startSketchOn('YZ')
|> close(%)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/lots_of_planes.png", &result, 0.999);
}
@ -777,7 +758,7 @@ async fn serial_test_holes() {
|> extrude(2, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/holes.png", &result, 0.999);
}
@ -796,7 +777,7 @@ async fn optional_params() {
const thing = other_circle([2, 2], 20)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/optional_params.png", &result, 0.999);
}
@ -832,7 +813,7 @@ const part = roundedRectangle([0, 0], 20, 20, 4)
|> extrude(2, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/rounded_with_holes.png", &result, 0.999);
}
@ -840,7 +821,7 @@ const part = roundedRectangle([0, 0], 20, 20, 4)
async fn serial_test_top_level_expression() {
let code = r#"startSketchOn('XY') |> circle([0,0], 22, %) |> extrude(14, %)"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/top_level_expression.png", &result, 0.999);
}
@ -854,7 +835,7 @@ const part = startSketchOn('XY')
|> extrude(1, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/patterns_linear_basic_with_math.png",
&result,
@ -870,7 +851,7 @@ async fn serial_test_patterns_linear_basic() {
|> extrude(1, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic.png", &result, 0.999);
}
@ -886,7 +867,7 @@ async fn serial_test_patterns_linear_basic_3d() {
|> patternLinear3d({axis: [1, 0, 1], repetitions: 3, distance: 6}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic_3d.png", &result, 0.999);
}
@ -898,7 +879,7 @@ async fn serial_test_patterns_linear_basic_negative_distance() {
|> extrude(1, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/patterns_linear_basic_negative_distance.png",
&result,
@ -914,7 +895,7 @@ async fn serial_test_patterns_linear_basic_negative_axis() {
|> extrude(1, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/patterns_linear_basic_negative_axis.png",
&result,
@ -939,7 +920,7 @@ const rectangle = startSketchOn('XY')
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic_holes.png", &result, 0.999);
}
@ -951,7 +932,7 @@ async fn serial_test_patterns_circular_basic_2d() {
|> extrude(1, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/patterns_circular_basic_2d.png", &result, 0.999);
}
@ -967,7 +948,7 @@ async fn serial_test_patterns_circular_basic_3d() {
|> patternCircular3d({axis: [0,0, 1], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/patterns_circular_basic_3d.png", &result, 0.999);
}
@ -983,7 +964,7 @@ async fn serial_test_patterns_circular_3d_tilted_axis() {
|> patternCircular3d({axis: [1,1,0], center: [10, 0, 10], repetitions: 10, arcDegrees: 360, rotateDuplicates: true}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/patterns_circular_3d_tilted_axis.png",
&result,
@ -995,7 +976,7 @@ async fn serial_test_patterns_circular_3d_tilted_axis() {
async fn serial_test_import_file_doesnt_exist() {
let code = r#"const model = import("thing.obj")"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
@ -1007,7 +988,7 @@ async fn serial_test_import_file_doesnt_exist() {
async fn serial_test_import_obj_with_mtl() {
let code = r#"const model = import("tests/executor/inputs/cube.obj")"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/import_obj_with_mtl.png", &result, 0.999);
}
@ -1015,7 +996,7 @@ async fn serial_test_import_obj_with_mtl() {
async fn serial_test_import_obj_with_mtl_units() {
let code = r#"const model = import("tests/executor/inputs/cube.obj", {type: "obj", units: "m"})"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/import_obj_with_mtl_units.png", &result, 0.999);
}
@ -1023,7 +1004,7 @@ async fn serial_test_import_obj_with_mtl_units() {
async fn serial_test_import_gltf_with_bin() {
let code = r#"const model = import("tests/executor/inputs/cube.gltf")"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/import_gltf_with_bin.png", &result, 0.999);
}
@ -1031,7 +1012,7 @@ async fn serial_test_import_gltf_with_bin() {
async fn serial_test_import_gltf_embedded() {
let code = r#"const model = import("tests/executor/inputs/cube-embedded.gltf")"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/import_gltf_embedded.png", &result, 0.999);
}
@ -1039,7 +1020,7 @@ async fn serial_test_import_gltf_embedded() {
async fn serial_test_import_glb() {
let code = r#"const model = import("tests/executor/inputs/cube.glb")"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/import_glb.png", &result, 0.999);
}
@ -1047,7 +1028,7 @@ async fn serial_test_import_glb() {
async fn serial_test_import_glb_no_assign() {
let code = r#"import("tests/executor/inputs/cube.glb")"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/import_glb_no_assign.png", &result, 0.999);
}
@ -1055,7 +1036,7 @@ async fn serial_test_import_glb_no_assign() {
async fn serial_test_import_ext_doesnt_match() {
let code = r#"const model = import("tests/executor/inputs/cube.gltf", {type: "obj", units: "m"})"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
@ -1080,7 +1061,7 @@ async fn serial_test_cube_mm() {
const myCube = cube([0,0], 10)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/cube_mm.png", &result, 1.0);
}
@ -1213,7 +1194,7 @@ const part002 = startSketchOn(part001, "here")
|> extrude(1, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
@ -1254,7 +1235,7 @@ const part003 = startSketchOn(part002, "end")
|> extrude(5, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_of_face.png", &result, 1.0);
}
@ -1271,7 +1252,7 @@ async fn serial_test_stdlib_kcl_error_right_code_path() {
|> extrude(2, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
@ -1299,7 +1280,7 @@ const part002 = startSketchOn(part001, "end")
|> extrude(5, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_circle.png", &result, 1.0);
}
@ -1323,7 +1304,7 @@ const part002 = startSketchOn(part001, "end")
|> extrude(5, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_circle_tagged.png", &result, 1.0);
}
@ -1365,7 +1346,7 @@ const part = rectShape([0, 0], 20, 20)
}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
@ -1386,7 +1367,7 @@ async fn serial_test_big_number_angle_to_match_length_x() {
|> extrude(10, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/big_number_angle_to_match_length_x.png",
&result,
@ -1407,7 +1388,7 @@ async fn serial_test_big_number_angle_to_match_length_y() {
|> extrude(10, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/big_number_angle_to_match_length_y.png",
&result,
@ -1431,7 +1412,7 @@ async fn serial_test_simple_revolve() {
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve.png", &result, 1.0);
}
@ -1451,7 +1432,7 @@ async fn serial_test_simple_revolve_uppercase() {
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_uppercase.png", &result, 1.0);
}
@ -1471,7 +1452,7 @@ async fn serial_test_simple_revolve_negative() {
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_negative.png", &result, 1.0);
}
@ -1491,7 +1472,7 @@ async fn serial_test_revolve_bad_angle_low() {
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
@ -1516,7 +1497,7 @@ async fn serial_test_revolve_bad_angle_high() {
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
@ -1541,7 +1522,7 @@ async fn serial_test_simple_revolve_custom_angle() {
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_custom_angle.png", &result, 1.0);
}
@ -1561,7 +1542,7 @@ async fn serial_test_simple_revolve_custom_axis() {
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_custom_axis.png", &result, 1.0);
}
@ -1585,7 +1566,7 @@ const sketch001 = startSketchOn(box, "end")
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_edge.png", &result, 1.0);
}
@ -1609,7 +1590,7 @@ const sketch001 = startSketchOn(box, "revolveAxis")
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
@ -1636,7 +1617,7 @@ const sketch001 = startSketchOn(box, "END")
}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face_circle_edge.png", &result, 1.0);
}
@ -1658,7 +1639,7 @@ const sketch001 = startSketchOn(box, "END")
}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face_circle.png", &result, 1.0);
}
@ -1684,7 +1665,7 @@ const sketch001 = startSketchOn(box, "end")
}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face.png", &result, 1.0);
}
@ -1698,7 +1679,7 @@ async fn serial_test_basic_revolve_circle() {
}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/basic_revolve_circle.png", &result, 1.0);
}
@ -1725,7 +1706,7 @@ const part002 = startSketchOn(part001, 'end')
|> extrude(5, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_sketch_on_edge.png", &result, 1.0);
}
@ -1786,7 +1767,7 @@ const plumbus1 = make_circle(p, 'b', [0, 0], 2.5)
}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/plumbus_fillets.png", &result, 1.0);
}
@ -1794,7 +1775,7 @@ const plumbus1 = make_circle(p, 'b', [0, 0], 2.5)
async fn serial_test_empty_file_is_ok() {
let code = r#""#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_ok());
}
@ -1824,7 +1805,7 @@ async fn serial_test_member_expression_in_params() {
capScrew([0, 0.5, 0], 50, 37.5, 50, 25)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/member_expression_in_params.png", &result, 1.0);
}
@ -1869,7 +1850,7 @@ const bracket = startSketchOn('XY')
}, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
@ -1893,7 +1874,7 @@ const secondSketch = startSketchOn(part001, '')
|> extrude(20, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
@ -1924,7 +1905,7 @@ const extrusion = startSketchOn('XY')
|> extrude(height, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await;
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
@ -1942,7 +1923,7 @@ async fn serial_test_xz_plane() {
|> extrude(5 + 7, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/xz_plane.png", &result, 1.0);
}
@ -1956,6 +1937,38 @@ async fn serial_test_neg_xz_plane() {
|> extrude(5 + 7, %)
"#;
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/neg_xz_plane.png", &result, 1.0);
}
async fn snapshot_on_test_server(code: String, test_name: &str) -> Result<image::DynamicImage> {
let default_addr = "0.0.0.0:3333";
let server_url = std::env::var("TEST_EXECUTOR_ADDR").unwrap_or(default_addr.to_owned());
let client = reqwest::Client::new();
let body = serde_json::json!({
"kcl_program": code,
"test_name": test_name,
});
let body = serde_json::to_vec(&body).unwrap();
let resp = match client.post(format!("http://{server_url}")).body(body).send().await {
Ok(r) => r,
Err(e) if e.is_connect() => {
eprintln!("Received a connection error. Is the test server running?");
eprintln!("If so, is it running at {server_url}?");
panic!("Connection error: {e}");
}
Err(e) => panic!("{e}"),
};
let status = resp.status();
let bytes = resp.bytes().await?;
if status.is_success() {
let img = ImageReader::new(Cursor::new(bytes)).with_guessed_format()?.decode()?;
Ok(img)
} else if status == hyper::StatusCode::BAD_GATEWAY {
let err = String::from_utf8(bytes.into()).unwrap();
anyhow::bail!("{err}")
} else {
let err = String::from_utf8(bytes.into()).unwrap();
panic!("{err}");
}
}