Compare commits
12 Commits
v0.26.2
...
achalmers/
Author | SHA1 | Date | |
---|---|---|---|
f297e0827e | |||
70023a31ef | |||
fb0def6797 | |||
793e3cfa95 | |||
7f25c4ebed | |||
e66204398f | |||
255dbc70da | |||
6e93375f26 | |||
28b30127cb | |||
8064ac8b31 | |||
c3040aa053 | |||
c12b5b67ee |
14
.github/workflows/cargo-test.yml
vendored
@ -59,11 +59,21 @@ 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 }}"
|
||||
cargo nextest archive --archive-file tests.tar.zst --workspace --profile ci
|
||||
- name: Start test KCL server
|
||||
run: |-
|
||||
cd "${{ matrix.dir }}"
|
||||
cargo build --quiet --bin kcl-test-server --workspace && ./target/debug/kcl-test-server &
|
||||
env:
|
||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||
- 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 --lcov --output-path lcov.info --test-threads=1 --no-fail-fast --profile ci --archive-file tests.tar.zst 2>&1 | tee /tmp/github-actions.log
|
||||
env:
|
||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||
RUST_MIN_STACK: 10485760000
|
||||
|
@ -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)"
|
||||
|
23
src/wasm-lib/Cargo.lock
generated
@ -1434,6 +1434,19 @@ dependencies = [
|
||||
"syn 2.0.66",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kcl-test-server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hyper",
|
||||
"kcl-lib",
|
||||
"pico-args",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.3.5"
|
||||
@ -1815,6 +1828,12 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.5"
|
||||
@ -2492,9 +2511,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",
|
||||
|
@ -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
@ -0,0 +1,2 @@
|
||||
start-test-server:
|
||||
cargo build --quiet --bin kcl-test-server --workspace && ./target/debug/kcl-test-server
|
13
src/wasm-lib/kcl-test-server/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[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" }
|
||||
pico-args = "0.5.0"
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
serde_json = "1.0.117"
|
||||
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }
|
210
src/wasm-lib/kcl-test-server/src/main.rs
Normal file
@ -0,0 +1,210 @@
|
||||
//! Executes KCL programs.
|
||||
//! The server reuses the same engine session for each KCL program it receives.
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use hyper::body::Bytes;
|
||||
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;
|
||||
use kcl_lib::test_server::RequestBody;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
// Parse the CLI arguments.
|
||||
let pargs = pico_args::Arguments::from_env();
|
||||
let args = ServerArgs::parse(pargs)?;
|
||||
// Run the actual server.
|
||||
start_server(args).await
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ServerArgs {
|
||||
/// What port this server should listen on.
|
||||
listen_on: SocketAddr,
|
||||
/// How many connections to establish with the engine.
|
||||
num_engine_conns: u8,
|
||||
}
|
||||
|
||||
impl ServerArgs {
|
||||
fn parse(mut pargs: pico_args::Arguments) -> Result<Self, pico_args::Error> {
|
||||
let args = ServerArgs {
|
||||
listen_on: pargs
|
||||
.opt_value_from_str("--listen-on")?
|
||||
.unwrap_or("0.0.0.0:3333".parse().unwrap()),
|
||||
num_engine_conns: pargs.opt_value_from_str("--num-engine-conns")?.unwrap_or(1),
|
||||
};
|
||||
println!("Config is {args:?}");
|
||||
Ok(args)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sent from the server to each worker.
|
||||
struct WorkerReq {
|
||||
/// A KCL program, in UTF-8.
|
||||
body: Bytes,
|
||||
/// A channel to send the HTTP response back.
|
||||
resp: oneshot::Sender<Response<Body>>,
|
||||
}
|
||||
|
||||
/// Each worker has a connection to the engine, and accepts
|
||||
/// KCL programs. When it receives one (over the mpsc channel)
|
||||
/// it executes it and returns the result via a oneshot channel.
|
||||
fn start_worker(i: u8) -> mpsc::Sender<WorkerReq> {
|
||||
println!("Starting worker {i}");
|
||||
// Make a work queue for this worker.
|
||||
let (tx, mut rx) = mpsc::channel(1);
|
||||
tokio::task::spawn(async move {
|
||||
let state = ExecutorContext::new_for_unit_test(UnitLength::Mm).await.unwrap();
|
||||
println!("Worker {i} ready");
|
||||
while let Some(req) = rx.recv().await {
|
||||
let req: WorkerReq = req;
|
||||
let resp = snapshot_endpoint(req.body, state.clone()).await;
|
||||
if req.resp.send(resp).is_err() {
|
||||
println!("\tWorker {i} exiting");
|
||||
}
|
||||
}
|
||||
println!("\tWorker {i} exiting");
|
||||
});
|
||||
tx
|
||||
}
|
||||
|
||||
struct ServerState {
|
||||
workers: Vec<mpsc::Sender<WorkerReq>>,
|
||||
req_num: AtomicUsize,
|
||||
}
|
||||
|
||||
async fn start_server(args: ServerArgs) -> anyhow::Result<()> {
|
||||
let ServerArgs {
|
||||
listen_on,
|
||||
num_engine_conns,
|
||||
} = args;
|
||||
let workers: Vec<_> = (0..num_engine_conns).map(start_worker).collect();
|
||||
let state = Arc::new(ServerState {
|
||||
workers,
|
||||
req_num: 0.into(),
|
||||
});
|
||||
// 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 state = 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 state = state.clone();
|
||||
async move { handle_request(req, state).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(())
|
||||
}
|
||||
|
||||
async fn handle_request(req: hyper::Request<Body>, state3: Arc<ServerState>) -> Result<Response<Body>, Error> {
|
||||
let body = hyper::body::to_bytes(req.into_body()).await?;
|
||||
|
||||
// Round robin requests between each available worker.
|
||||
let req_num = state3.req_num.fetch_add(1, Ordering::Relaxed);
|
||||
let worker_id = req_num % state3.workers.len();
|
||||
// println!("Sending request {req_num} to worker {worker_id}");
|
||||
let worker = state3.workers[worker_id].clone();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let req_sent = worker.send(WorkerReq { body, resp: tx }).await;
|
||||
req_sent.unwrap();
|
||||
let resp = rx.await.unwrap();
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
/// 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: Bytes, state: ExecutorContext) -> Response<Body> {
|
||||
let body = match serde_json::from_slice::<RequestBody>(body.as_ref()) {
|
||||
Ok(bd) => bd,
|
||||
Err(e) => return bad_request(format!("Invalid request JSON: {e}")),
|
||||
};
|
||||
let RequestBody { kcl_program, test_name } = body;
|
||||
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}")),
|
||||
};
|
||||
eprintln!("Executing {test_name}");
|
||||
if let Err(e) = state.reset_scene().await {
|
||||
return kcl_err(e);
|
||||
}
|
||||
// Let users know if the test is taking a long time.
|
||||
let (done_tx, done_rx) = oneshot::channel::<()>();
|
||||
let timer = time_until(done_rx);
|
||||
let snapshot = match state.execute_and_prepare_snapshot(program).await {
|
||||
Ok(sn) => sn,
|
||||
Err(e) => return kcl_err(e),
|
||||
};
|
||||
let _ = done_tx.send(());
|
||||
timer.abort();
|
||||
eprintln!("\tServing 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> {
|
||||
eprintln!("\tBad request");
|
||||
let mut resp = Response::new(Body::from(msg));
|
||||
*resp.status_mut() = hyper::StatusCode::BAD_REQUEST;
|
||||
resp
|
||||
}
|
||||
|
||||
fn bad_gateway(msg: String) -> Response<Body> {
|
||||
eprintln!("\tBad gateway");
|
||||
let mut resp = Response::new(Body::from(msg));
|
||||
*resp.status_mut() = hyper::StatusCode::BAD_GATEWAY;
|
||||
resp
|
||||
}
|
||||
|
||||
fn kcl_err(err: anyhow::Error) -> Response<Body> {
|
||||
eprintln!("\tBad KCL");
|
||||
bad_gateway(format!("{err}"))
|
||||
}
|
||||
|
||||
fn time_until(done: oneshot::Receiver<()>) -> JoinHandle<()> {
|
||||
tokio::task::spawn(async move {
|
||||
let period = 10;
|
||||
tokio::pin!(done);
|
||||
for i in 1..=3 {
|
||||
tokio::select! {
|
||||
biased;
|
||||
// If the test is done, no need for this timer anymore.
|
||||
_ = &mut done => return,
|
||||
_ = sleep(Duration::from_secs(period)) => {
|
||||
eprintln!("\tTest has taken {}s", period * i);
|
||||
},
|
||||
};
|
||||
}
|
||||
})
|
||||
}
|
@ -40,23 +40,54 @@ pub struct TcpRead {
|
||||
stream: futures::stream::SplitStream<tokio_tungstenite::WebSocketStream<reqwest::Upgraded>>,
|
||||
}
|
||||
|
||||
/// Occurs when client couldn't read from the WebSocket to the engine.
|
||||
// #[derive(Debug)]
|
||||
pub enum WebSocketReadError {
|
||||
/// Could not read a message due to WebSocket errors.
|
||||
Read(tokio_tungstenite::tungstenite::Error),
|
||||
/// WebSocket message didn't contain a valid message that the KCL Executor could parse.
|
||||
Deser(anyhow::Error),
|
||||
}
|
||||
|
||||
impl From<anyhow::Error> for WebSocketReadError {
|
||||
fn from(e: anyhow::Error) -> Self {
|
||||
Self::Deser(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl TcpRead {
|
||||
pub async fn read(&mut self) -> Result<WebSocketResponse> {
|
||||
pub async fn read(&mut self) -> std::result::Result<WebSocketResponse, WebSocketReadError> {
|
||||
let Some(msg) = self.stream.next().await else {
|
||||
anyhow::bail!("Failed to read from websocket");
|
||||
return Err(anyhow::anyhow!("Failed to read from WebSocket").into());
|
||||
};
|
||||
let msg: WebSocketResponse = match msg? {
|
||||
WsMsg::Text(text) => serde_json::from_str(&text)?,
|
||||
WsMsg::Binary(bin) => bson::from_slice(&bin)?,
|
||||
other => anyhow::bail!("Unexpected websocket message from server: {}", other),
|
||||
let msg = match msg {
|
||||
Ok(msg) => msg,
|
||||
Err(e) if matches!(e, tokio_tungstenite::tungstenite::Error::Protocol(_)) => {
|
||||
return Err(WebSocketReadError::Read(e))
|
||||
}
|
||||
Err(e) => return Err(anyhow::anyhow!("Error reading from engine's WebSocket: {e}").into()),
|
||||
};
|
||||
let msg: WebSocketResponse = match msg {
|
||||
WsMsg::Text(text) => serde_json::from_str(&text)
|
||||
.map_err(anyhow::Error::from)
|
||||
.map_err(WebSocketReadError::from)?,
|
||||
WsMsg::Binary(bin) => bson::from_slice(&bin)
|
||||
.map_err(anyhow::Error::from)
|
||||
.map_err(WebSocketReadError::from)?,
|
||||
other => return Err(anyhow::anyhow!("Unexpected WebSocket message from engine API: {other}").into()),
|
||||
};
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TcpReadHandle {
|
||||
handle: Arc<tokio::task::JoinHandle<Result<()>>>,
|
||||
handle: Arc<tokio::task::JoinHandle<Result<(), WebSocketReadError>>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for TcpReadHandle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "TcpReadHandle")
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TcpReadHandle {
|
||||
@ -150,14 +181,17 @@ impl EngineConnection {
|
||||
match tcp_read.read().await {
|
||||
Ok(ws_resp) => {
|
||||
for e in ws_resp.errors.iter().flatten() {
|
||||
println!("got error message: {e}");
|
||||
println!("got error message: {} {}", e.error_code, e.message);
|
||||
}
|
||||
if let Some(id) = ws_resp.request_id {
|
||||
responses_clone.insert(id, ws_resp.clone());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("got ws error: {:?}", e);
|
||||
match &e {
|
||||
WebSocketReadError::Read(e) => eprintln!("could not read from WS: {:?}", e),
|
||||
WebSocketReadError::Deser(e) => eprintln!("could not deserialize msg from WS: {:?}", e),
|
||||
}
|
||||
*socket_health_tcp_read.lock().unwrap() = SocketHealth::Inactive;
|
||||
return Err(e);
|
||||
}
|
||||
|
@ -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,57 @@ impl ExecutorContext {
|
||||
})
|
||||
}
|
||||
|
||||
/// For executing unit tests.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
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(),
|
||||
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 +1361,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;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ pub mod lsp;
|
||||
pub mod parser;
|
||||
pub mod settings;
|
||||
pub mod std;
|
||||
pub mod test_server;
|
||||
pub mod thread;
|
||||
pub mod token;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
8
src/wasm-lib/kcl/src/test_server.rs
Normal file
@ -0,0 +1,8 @@
|
||||
//! Types used to send data to the test server.
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub struct RequestBody {
|
||||
pub kcl_program: String,
|
||||
#[serde(default)]
|
||||
pub test_name: String,
|
||||
}
|
@ -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);
|
||||
macro_rules! test_name {
|
||||
() => {{
|
||||
fn f() {}
|
||||
fn type_name_of<T>(_: T) -> &'static str {
|
||||
std::any::type_name::<T>()
|
||||
}
|
||||
|
||||
let ctx = ExecutorContext::new(
|
||||
&client,
|
||||
ExecutorSettings {
|
||||
units,
|
||||
highlight_edges: true,
|
||||
enable_ssao: false,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(ctx)
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 250 KiB After Width: | Height: | Size: 289 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 141 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 80 KiB |
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 210 KiB |
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 210 KiB |
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 214 KiB |
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 183 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 176 KiB |
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 125 KiB |
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 162 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 198 KiB |
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 113 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 138 KiB |
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 129 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 144 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 166 KiB |
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 195 KiB After Width: | Height: | Size: 195 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 181 KiB |
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 146 KiB |
Before Width: | Height: | Size: 195 KiB After Width: | Height: | Size: 190 KiB |
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 157 KiB |
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 165 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 164 KiB |
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 115 KiB |