Compare commits
2 Commits
achalmers/
...
v0.22.2
Author | SHA1 | Date | |
---|---|---|---|
0add26cf61 | |||
b54fc534c2 |
14
.github/workflows/cargo-test.yml
vendored
@ -59,21 +59,11 @@ jobs:
|
||||
- uses: taiki-e/install-action@nextest
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2.6.1
|
||||
- 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
|
||||
- name: cargo test
|
||||
shell: bash
|
||||
run: |-
|
||||
cd "${{ matrix.dir }}"
|
||||
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
|
||||
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
|
||||
env:
|
||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||
RUST_MIN_STACK: 10485760000
|
||||
|
@ -9,7 +9,7 @@ A circular pattern on a 2D sketch.
|
||||
|
||||
|
||||
```js
|
||||
patternCircular2d(data: CircularPattern2dData, sketch_group: SketchGroup) -> [SketchGroup]
|
||||
patternCircular2d(data: CircularPattern2dData, sketch_group_set: SketchGroupSet) -> [SketchGroup]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@ -48,7 +48,7 @@ const example = extrude(1, exampleSketch)
|
||||
rotateDuplicates: string,
|
||||
}
|
||||
```
|
||||
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. (REQUIRED)
|
||||
* `sketch_group_set`: `SketchGroupSet` - A sketch group or a group of sketch groups. (REQUIRED)
|
||||
```js
|
||||
{
|
||||
// The plane id or face id of the sketch group.
|
||||
@ -129,6 +129,7 @@ const example = extrude(1, exampleSketch)
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
type: "sketchGroup",
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
// The from point.
|
||||
@ -212,6 +213,9 @@ const example = extrude(1, exampleSketch)
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
} |
|
||||
{
|
||||
type: "sketchGroups",
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -9,7 +9,7 @@ A circular pattern on a 3D model.
|
||||
|
||||
|
||||
```js
|
||||
patternCircular3d(data: CircularPattern3dData, extrude_group: ExtrudeGroup) -> [ExtrudeGroup]
|
||||
patternCircular3d(data: CircularPattern3dData, extrude_group_set: ExtrudeGroupSet) -> [ExtrudeGroup]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@ -47,7 +47,7 @@ const example = extrude(-5, exampleSketch)
|
||||
rotateDuplicates: string,
|
||||
}
|
||||
```
|
||||
* `extrude_group`: `ExtrudeGroup` - An extrude group is a collection of extrude surfaces. (REQUIRED)
|
||||
* `extrude_group_set`: `ExtrudeGroupSet` - A extrude group or a group of extrude groups. (REQUIRED)
|
||||
```js
|
||||
{
|
||||
// The id of the extrusion end cap
|
||||
@ -127,6 +127,7 @@ const example = extrude(-5, exampleSketch)
|
||||
}],
|
||||
// The id of the extrusion start cap
|
||||
startCapId: uuid,
|
||||
type: "extrudeGroup",
|
||||
// The extrude surfaces.
|
||||
value: [{
|
||||
// The face id for the extrude plane.
|
||||
@ -176,6 +177,9 @@ const example = extrude(-5, exampleSketch)
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
} |
|
||||
{
|
||||
type: "extrudeGroups",
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -9,7 +9,7 @@ A linear pattern on a 3D model.
|
||||
|
||||
|
||||
```js
|
||||
patternLinear3d(data: LinearPattern3dData, extrude_group: ExtrudeGroup) -> [ExtrudeGroup]
|
||||
patternLinear3d(data: LinearPattern3dData, extrude_group_set: ExtrudeGroupSet) -> [ExtrudeGroup]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@ -45,7 +45,7 @@ const example = extrude(1, exampleSketch)
|
||||
repetitions: number,
|
||||
}
|
||||
```
|
||||
* `extrude_group`: `ExtrudeGroup` - An extrude group is a collection of extrude surfaces. (REQUIRED)
|
||||
* `extrude_group_set`: `ExtrudeGroupSet` - A extrude group or a group of extrude groups. (REQUIRED)
|
||||
```js
|
||||
{
|
||||
// The id of the extrusion end cap
|
||||
@ -125,6 +125,7 @@ const example = extrude(1, exampleSketch)
|
||||
}],
|
||||
// The id of the extrusion start cap
|
||||
startCapId: uuid,
|
||||
type: "extrudeGroup",
|
||||
// The extrude surfaces.
|
||||
value: [{
|
||||
// The face id for the extrude plane.
|
||||
@ -174,6 +175,9 @@ const example = extrude(1, exampleSketch)
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
} |
|
||||
{
|
||||
type: "extrudeGroups",
|
||||
}
|
||||
```
|
||||
|
||||
|
6941
docs/kcl/std.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.22.1",
|
||||
"version": "0.22.2",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.16.0",
|
||||
|
1639
src-tauri/Cargo.lock
generated
@ -20,7 +20,7 @@ kittycad = "0.3.5"
|
||||
log = "0.4.21"
|
||||
oauth2 = "4.4.2"
|
||||
serde_json = "1.0"
|
||||
tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] }
|
||||
tauri = { version = "2.0.0-beta.22", features = [ "devtools", "unstable"] }
|
||||
tauri-plugin-cli = { version = "2.0.0-beta.3" }
|
||||
tauri-plugin-deep-link = { version = "2.0.0-beta.3" }
|
||||
tauri-plugin-dialog = { version = "2.0.0-beta.6" }
|
||||
|
@ -63,16 +63,17 @@
|
||||
"subcommands": {}
|
||||
},
|
||||
"deep-link": {
|
||||
"domains": [
|
||||
{
|
||||
"host": "app.zoo.dev"
|
||||
}
|
||||
]
|
||||
"mobile": [],
|
||||
"desktop": {
|
||||
"schemes": [
|
||||
"app.zoo.dev"
|
||||
]
|
||||
}
|
||||
},
|
||||
"shell": {
|
||||
"open": true
|
||||
}
|
||||
},
|
||||
"productName": "Zoo Modeling App",
|
||||
"version": "0.22.1"
|
||||
"version": "0.22.2"
|
||||
}
|
||||
|
@ -1,8 +1,3 @@
|
||||
# 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.
|
||||
@ -19,20 +14,12 @@ slow-timeout = { period = "50s", terminate-after = 5 }
|
||||
[[profile.default.overrides]]
|
||||
filter = "test(serial_test_)"
|
||||
test-group = "serial-integration"
|
||||
threads-required = 4
|
||||
|
||||
# [[profile.default.scripts]]
|
||||
# filter = 'test(serial_test_)'
|
||||
# setup = 'test-server'
|
||||
threads-required = 2
|
||||
|
||||
[[profile.ci.overrides]]
|
||||
filter = "test(serial_test_)"
|
||||
test-group = "serial-integration"
|
||||
threads-required = 4
|
||||
|
||||
# [[profile.default.scripts]]
|
||||
# filter = 'test(serial_test_)'
|
||||
# setup = 'test-server'
|
||||
threads-required = 2
|
||||
|
||||
[[profile.default.overrides]]
|
||||
filter = "test(parser::parser_impl::snapshot_tests)"
|
||||
|
25
src/wasm-lib/Cargo.lock
generated
@ -1369,7 +1369,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.1.59"
|
||||
version = "0.1.60"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx",
|
||||
@ -1434,19 +1434,6 @@ 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"
|
||||
@ -1828,12 +1815,6 @@ 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"
|
||||
@ -2511,9 +2492,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.117"
|
||||
version = "1.0.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
|
||||
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.13"
|
||||
toml = "0.8.14"
|
||||
uuid = { version = "1.8.0", features = ["v4", "js", "serde"] }
|
||||
wasm-bindgen = "0.2.91"
|
||||
wasm-bindgen-futures = "0.4.42"
|
||||
@ -65,7 +65,6 @@ members = [
|
||||
"derive-docs",
|
||||
"kcl",
|
||||
"kcl-macros",
|
||||
"kcl-test-server",
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
|
@ -1,2 +0,0 @@
|
||||
start-test-server:
|
||||
cargo build --quiet --bin kcl-test-server --workspace && ./target/debug/kcl-test-server
|
@ -1,13 +0,0 @@
|
||||
[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"] }
|
@ -1,210 +0,0 @@
|
||||
//! 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);
|
||||
},
|
||||
};
|
||||
}
|
||||
})
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.1.59"
|
||||
version = "0.1.60"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -63,9 +63,10 @@ impl StdLibFnArg {
|
||||
|
||||
pub fn get_autocomplete_snippet(&self, index: usize) -> Result<Option<(usize, String)>> {
|
||||
if self.type_ == "SketchGroup"
|
||||
|| self.type_ == "ExtrudeGroup"
|
||||
|| self.type_ == "SketchSurface"
|
||||
|| self.type_ == "SketchGroupSet"
|
||||
|| self.type_ == "ExtrudeGroup"
|
||||
|| self.type_ == "ExtrudeGroupSet"
|
||||
|| self.type_ == "SketchSurface"
|
||||
{
|
||||
return Ok(Some((index, format!("${{{}:{}}}", index, "%"))));
|
||||
}
|
||||
|
@ -40,54 +40,23 @@ 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) -> std::result::Result<WebSocketResponse, WebSocketReadError> {
|
||||
pub async fn read(&mut self) -> Result<WebSocketResponse> {
|
||||
let Some(msg) = self.stream.next().await else {
|
||||
return Err(anyhow::anyhow!("Failed to read from WebSocket").into());
|
||||
anyhow::bail!("Failed to read from websocket");
|
||||
};
|
||||
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()),
|
||||
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),
|
||||
};
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TcpReadHandle {
|
||||
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")
|
||||
}
|
||||
handle: Arc<tokio::task::JoinHandle<Result<()>>>,
|
||||
}
|
||||
|
||||
impl Drop for TcpReadHandle {
|
||||
@ -181,17 +150,14 @@ impl EngineConnection {
|
||||
match tcp_read.read().await {
|
||||
Ok(ws_resp) => {
|
||||
for e in ws_resp.errors.iter().flatten() {
|
||||
println!("got error message: {} {}", e.error_code, e.message);
|
||||
println!("got error message: {e}");
|
||||
}
|
||||
if let Some(id) = ws_resp.request_id {
|
||||
responses_clone.insert(id, ws_resp.clone());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
match &e {
|
||||
WebSocketReadError::Read(e) => eprintln!("could not read from WS: {:?}", e),
|
||||
WebSocketReadError::Deser(e) => eprintln!("could not deserialize msg from WS: {:?}", e),
|
||||
}
|
||||
println!("got ws error: {:?}", e);
|
||||
*socket_health_tcp_read.lock().unwrap() = SocketHealth::Inactive;
|
||||
return Err(e);
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ use crate::{
|
||||
engine::EngineManager,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
fs::FileManager,
|
||||
settings::types::UnitLength,
|
||||
std::{FunctionKind, StdLib},
|
||||
};
|
||||
|
||||
@ -993,7 +992,7 @@ pub struct ExecutorContext {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExecutorSettings {
|
||||
/// The unit to use in modeling dimensions.
|
||||
pub units: UnitLength,
|
||||
pub units: crate::settings::types::UnitLength,
|
||||
/// Highlight edges of 3D objects?
|
||||
pub highlight_edges: bool,
|
||||
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
|
||||
@ -1084,57 +1083,6 @@ 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.
|
||||
@ -1361,7 +1309,7 @@ impl ExecutorContext {
|
||||
}
|
||||
|
||||
/// Update the units for the executor.
|
||||
pub fn update_units(&mut self, units: UnitLength) {
|
||||
pub fn update_units(&mut self, units: crate::settings::types::UnitLength) {
|
||||
self.settings.units = units;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@ 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")]
|
||||
|
@ -31,7 +31,8 @@ use crate::{
|
||||
docs::StdLibFn,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{
|
||||
ExecutorContext, ExtrudeGroup, MemoryItem, Metadata, SketchGroup, SketchGroupSet, SketchSurface, SourceRange,
|
||||
ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, MemoryItem, Metadata, SketchGroup, SketchGroupSet,
|
||||
SketchSurface, SourceRange,
|
||||
},
|
||||
std::{kcl_stdlib::KclStdLibFn, sketch::SketchOnFaceTag},
|
||||
};
|
||||
@ -773,6 +774,52 @@ impl Args {
|
||||
Ok((data, sketch_surface, tag))
|
||||
}
|
||||
|
||||
fn get_data_and_extrude_group_set<T: serde::de::DeserializeOwned>(&self) -> Result<(T, ExtrudeGroupSet), KclError> {
|
||||
let first_value = self
|
||||
.args
|
||||
.first()
|
||||
.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Expected a struct as the first argument, found `{:?}`", self.args),
|
||||
source_ranges: vec![self.source_range],
|
||||
})
|
||||
})?
|
||||
.get_json_value()?;
|
||||
|
||||
let data: T = serde_json::from_value(first_value).map_err(|e| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Failed to deserialize struct from JSON: {}", e),
|
||||
source_ranges: vec![self.source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let second_value = self.args.get(1).ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected an ExtrudeGroup as the second argument, found `{:?}`",
|
||||
self.args
|
||||
),
|
||||
source_ranges: vec![self.source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let extrude_set = if let MemoryItem::ExtrudeGroup(eg) = second_value {
|
||||
ExtrudeGroupSet::ExtrudeGroup(eg.clone())
|
||||
} else if let MemoryItem::ExtrudeGroups { value } = second_value {
|
||||
ExtrudeGroupSet::ExtrudeGroups(value.clone())
|
||||
} else {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected a ExtrudeGroup or Vector of ExtrudeGroups as the second argument, found `{:?}`",
|
||||
self.args
|
||||
),
|
||||
source_ranges: vec![self.source_range],
|
||||
}));
|
||||
};
|
||||
|
||||
Ok((data, extrude_set))
|
||||
}
|
||||
|
||||
fn get_data_and_extrude_group<T: serde::de::DeserializeOwned>(&self) -> Result<(T, Box<ExtrudeGroup>), KclError> {
|
||||
let first_value = self
|
||||
.args
|
||||
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{ExtrudeGroup, Geometries, Geometry, MemoryItem, SketchGroup, SketchGroupSet},
|
||||
executor::{ExtrudeGroup, ExtrudeGroupSet, Geometries, Geometry, MemoryItem, SketchGroup, SketchGroupSet},
|
||||
std::{types::Uint, Args},
|
||||
};
|
||||
|
||||
@ -141,7 +141,7 @@ async fn inner_pattern_linear_2d(
|
||||
|
||||
/// A linear pattern on a 3D model.
|
||||
pub async fn pattern_linear_3d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let (data, extrude_group): (LinearPattern3dData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
|
||||
let (data, extrude_group_set): (LinearPattern3dData, ExtrudeGroupSet) = args.get_data_and_extrude_group_set()?;
|
||||
|
||||
if data.axis == [0.0, 0.0, 0.0] {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
@ -152,7 +152,7 @@ pub async fn pattern_linear_3d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
}));
|
||||
}
|
||||
|
||||
let extrude_groups = inner_pattern_linear_3d(data, extrude_group, args).await?;
|
||||
let extrude_groups = inner_pattern_linear_3d(data, extrude_group_set, args).await?;
|
||||
Ok(MemoryItem::ExtrudeGroups { value: extrude_groups })
|
||||
}
|
||||
|
||||
@ -178,26 +178,36 @@ pub async fn pattern_linear_3d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
}]
|
||||
async fn inner_pattern_linear_3d(
|
||||
data: LinearPattern3dData,
|
||||
extrude_group: Box<ExtrudeGroup>,
|
||||
extrude_group_set: ExtrudeGroupSet,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
|
||||
let starting_extrude_groups = match extrude_group_set {
|
||||
ExtrudeGroupSet::ExtrudeGroup(extrude_group) => vec![extrude_group],
|
||||
ExtrudeGroupSet::ExtrudeGroups(extrude_groups) => extrude_groups,
|
||||
};
|
||||
|
||||
if args.ctx.is_mock {
|
||||
return Ok(vec![extrude_group.clone()]);
|
||||
return Ok(starting_extrude_groups);
|
||||
}
|
||||
|
||||
let geometries = pattern_linear(
|
||||
LinearPattern::ThreeD(data),
|
||||
Geometry::ExtrudeGroup(extrude_group),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
let mut extrude_groups = Vec::new();
|
||||
for extrude_group in starting_extrude_groups.iter() {
|
||||
let geometries = pattern_linear(
|
||||
LinearPattern::ThreeD(data.clone()),
|
||||
Geometry::ExtrudeGroup(extrude_group.clone()),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Geometries::ExtrudeGroups(extrude_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of extrude groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
let Geometries::ExtrudeGroups(new_extrude_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of extrude groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
|
||||
extrude_groups.extend(new_extrude_groups);
|
||||
}
|
||||
|
||||
Ok(extrude_groups)
|
||||
}
|
||||
@ -335,9 +345,9 @@ impl CircularPattern {
|
||||
|
||||
/// A circular pattern on a 2D sketch.
|
||||
pub async fn pattern_circular_2d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let (data, sketch_group): (CircularPattern2dData, Box<SketchGroup>) = args.get_data_and_sketch_group()?;
|
||||
let (data, sketch_group_set): (CircularPattern2dData, SketchGroupSet) = args.get_data_and_sketch_group_set()?;
|
||||
|
||||
let sketch_groups = inner_pattern_circular_2d(data, sketch_group, args).await?;
|
||||
let sketch_groups = inner_pattern_circular_2d(data, sketch_group_set, args).await?;
|
||||
Ok(MemoryItem::SketchGroups { value: sketch_groups })
|
||||
}
|
||||
|
||||
@ -364,35 +374,45 @@ pub async fn pattern_circular_2d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
}]
|
||||
async fn inner_pattern_circular_2d(
|
||||
data: CircularPattern2dData,
|
||||
sketch_group: Box<SketchGroup>,
|
||||
sketch_group_set: SketchGroupSet,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<SketchGroup>>, KclError> {
|
||||
let starting_sketch_groups = match sketch_group_set {
|
||||
SketchGroupSet::SketchGroup(sketch_group) => vec![sketch_group],
|
||||
SketchGroupSet::SketchGroups(sketch_groups) => sketch_groups,
|
||||
};
|
||||
|
||||
if args.ctx.is_mock {
|
||||
return Ok(vec![sketch_group]);
|
||||
return Ok(starting_sketch_groups);
|
||||
}
|
||||
|
||||
let geometries = pattern_circular(
|
||||
CircularPattern::TwoD(data),
|
||||
Geometry::SketchGroup(sketch_group),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
let mut sketch_groups = Vec::new();
|
||||
for sketch_group in starting_sketch_groups.iter() {
|
||||
let geometries = pattern_circular(
|
||||
CircularPattern::TwoD(data.clone()),
|
||||
Geometry::SketchGroup(sketch_group.clone()),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Geometries::SketchGroups(sketch_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of sketch groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
let Geometries::SketchGroups(new_sketch_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of sketch groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
|
||||
sketch_groups.extend(new_sketch_groups);
|
||||
}
|
||||
|
||||
Ok(sketch_groups)
|
||||
}
|
||||
|
||||
/// A circular pattern on a 3D model.
|
||||
pub async fn pattern_circular_3d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let (data, extrude_group): (CircularPattern3dData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
|
||||
let (data, extrude_group_set): (CircularPattern3dData, ExtrudeGroupSet) = args.get_data_and_extrude_group_set()?;
|
||||
|
||||
let extrude_groups = inner_pattern_circular_3d(data, extrude_group, args).await?;
|
||||
let extrude_groups = inner_pattern_circular_3d(data, extrude_group_set, args).await?;
|
||||
Ok(MemoryItem::ExtrudeGroups { value: extrude_groups })
|
||||
}
|
||||
|
||||
@ -416,26 +436,36 @@ pub async fn pattern_circular_3d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
}]
|
||||
async fn inner_pattern_circular_3d(
|
||||
data: CircularPattern3dData,
|
||||
extrude_group: Box<ExtrudeGroup>,
|
||||
extrude_group_set: ExtrudeGroupSet,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
|
||||
let starting_extrude_groups = match extrude_group_set {
|
||||
ExtrudeGroupSet::ExtrudeGroup(extrude_group) => vec![extrude_group],
|
||||
ExtrudeGroupSet::ExtrudeGroups(extrude_groups) => extrude_groups,
|
||||
};
|
||||
|
||||
if args.ctx.is_mock {
|
||||
return Ok(vec![extrude_group]);
|
||||
return Ok(starting_extrude_groups);
|
||||
}
|
||||
|
||||
let geometries = pattern_circular(
|
||||
CircularPattern::ThreeD(data),
|
||||
Geometry::ExtrudeGroup(extrude_group),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
let mut extrude_groups = Vec::new();
|
||||
for extrude_group in starting_extrude_groups.iter() {
|
||||
let geometries = pattern_circular(
|
||||
CircularPattern::ThreeD(data.clone()),
|
||||
Geometry::ExtrudeGroup(extrude_group.clone()),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Geometries::ExtrudeGroups(extrude_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of extrude groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
let Geometries::ExtrudeGroups(new_extrude_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of extrude groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
|
||||
extrude_groups.extend(new_extrude_groups);
|
||||
}
|
||||
|
||||
Ok(extrude_groups)
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
//! 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,39 +1,58 @@
|
||||
use std::io::Cursor;
|
||||
|
||||
use anyhow::Result;
|
||||
use image::io::Reader as ImageReader;
|
||||
use kcl_lib::{executor::ExecutorContext, settings::types::UnitLength};
|
||||
use kittycad::types::TakeSnapshot;
|
||||
use kcl_lib::{
|
||||
executor::{ExecutorContext, ExecutorSettings},
|
||||
settings::types::UnitLength,
|
||||
};
|
||||
|
||||
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()
|
||||
}};
|
||||
// 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)
|
||||
}
|
||||
|
||||
/// 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 = ExecutorContext::new_for_unit_test(units).await?;
|
||||
let ctx = new_context(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.
|
||||
@ -62,28 +81,28 @@ const part002 = startSketchOn(part001, "here")
|
||||
|> extrude(5, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/pipe_as_arg.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -118,14 +137,14 @@ const part002 = startSketchOn(part001, "start")
|
||||
|> extrude(5, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/mike_stress_test.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -153,7 +172,7 @@ const part002 = startSketchOn(part001, "END")
|
||||
|> extrude(5, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_end.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -181,7 +200,7 @@ const part002 = startSketchOn(part001, "END")
|
||||
|> extrude(-5, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/sketch_on_face_end_negative_extrude.png",
|
||||
&result,
|
||||
@ -201,7 +220,7 @@ async fn serial_test_fillet_duplicate_tags() {
|
||||
|> fillet({radius: 0.5, tags: ["thing", "thing"]}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
@ -221,7 +240,7 @@ async fn serial_test_basic_fillet_cube_start() {
|
||||
|> fillet({radius: 2, tags: ["thing", "thing2"]}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/basic_fillet_cube_start.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -238,7 +257,7 @@ async fn serial_test_basic_fillet_cube_end() {
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/basic_fillet_cube_end.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -255,7 +274,7 @@ async fn serial_test_basic_fillet_cube_close_opposite() {
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/basic_fillet_cube_close_opposite.png",
|
||||
&result,
|
||||
@ -275,7 +294,7 @@ async fn serial_test_basic_fillet_cube_next_adjacent() {
|
||||
|> fillet({radius: 2, tags: [getNextAdjacentEdge("thing3", %)]}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/basic_fillet_cube_next_adjacent.png",
|
||||
&result,
|
||||
@ -295,7 +314,7 @@ async fn serial_test_basic_fillet_cube_previous_adjacent() {
|
||||
|> fillet({radius: 2, tags: [getPreviousAdjacentEdge("thing3", %)]}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/basic_fillet_cube_previous_adjacent.png",
|
||||
&result,
|
||||
@ -320,7 +339,7 @@ async fn serial_test_execute_with_function_sketch() {
|
||||
const fnBox = box(3, 6, 10)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/function_sketch.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -340,7 +359,7 @@ async fn serial_test_execute_with_function_sketch_with_position() {
|
||||
|
||||
const thing = box([0,0], 3, 6, 10)"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/function_sketch_with_position.png",
|
||||
&result,
|
||||
@ -361,7 +380,7 @@ async fn serial_test_execute_with_angled_line() {
|
||||
|> extrude(4, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/angled_line.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -387,7 +406,7 @@ const bracket = startSketchOn('XY')
|
||||
|> extrude(width, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/parametric.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -421,7 +440,7 @@ const bracket = startSketchAt([0, 0])
|
||||
|> extrude(width, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/parametric_with_tan_arc.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -436,7 +455,7 @@ async fn serial_test_execute_engine_error_return() {
|
||||
|> extrude(4, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
@ -449,7 +468,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/i_shape.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -458,7 +477,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/pipes_on_pipes.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -466,7 +485,7 @@ async fn serial_test_execute_pipes_on_pipes() {
|
||||
async fn serial_test_execute_cylinder() {
|
||||
let code = include_str!("inputs/cylinder.kcl");
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/cylinder.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -474,7 +493,7 @@ async fn serial_test_execute_cylinder() {
|
||||
async fn serial_test_execute_kittycad_svg() {
|
||||
let code = include_str!("inputs/kittycad_svg.kcl");
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/kittycad_svg.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -499,7 +518,7 @@ const pt1 = b1.value[0]
|
||||
const pt2 = b2.value[0]
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/member_expression_sketch_group.png",
|
||||
&result,
|
||||
@ -515,7 +534,7 @@ async fn serial_test_helix_defaults() {
|
||||
|> helix({revolutions: 16, angle_start: 0}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/helix_defaults.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -527,7 +546,7 @@ async fn serial_test_helix_defaults_negative_extrude() {
|
||||
|> helix({revolutions: 16, angle_start: 0}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/helix_defaults_negative_extrude.png",
|
||||
&result,
|
||||
@ -543,7 +562,7 @@ async fn serial_test_helix_ccw() {
|
||||
|> helix({revolutions: 16, angle_start: 0, ccw: true}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/helix_ccw.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -555,7 +574,7 @@ async fn serial_test_helix_with_length() {
|
||||
|> helix({revolutions: 16, angle_start: 0, length: 3}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/helix_with_length.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -570,7 +589,7 @@ async fn serial_test_dimensions_match() {
|
||||
|> extrude(10, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/dimensions_match.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -587,7 +606,7 @@ const body = startSketchOn('XY')
|
||||
|> extrude(height, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/close_arc.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -613,7 +632,7 @@ box(10, 23, 8)
|
||||
let thing = box(-12, -15, 10)
|
||||
box(-20, -5, 10)"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/negative_args.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -626,7 +645,7 @@ async fn serial_test_basic_tangential_arc() {
|
||||
|> extrude(10, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -639,7 +658,7 @@ async fn serial_test_basic_tangential_arc_with_point() {
|
||||
|> extrude(10, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc_with_point.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -652,7 +671,7 @@ async fn serial_test_basic_tangential_arc_to() {
|
||||
|> extrude(10, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc_to.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -679,7 +698,7 @@ box(30, 43, 18, '-xy')
|
||||
let thing = box(-12, -15, 10, 'yz')
|
||||
box(-20, -5, 10, 'xy')"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/different_planes_same_drawing.png",
|
||||
&result,
|
||||
@ -741,7 +760,7 @@ const part004 = startSketchOn('YZ')
|
||||
|> close(%)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/lots_of_planes.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -758,7 +777,7 @@ async fn serial_test_holes() {
|
||||
|> extrude(2, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/holes.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -777,7 +796,7 @@ async fn optional_params() {
|
||||
|
||||
const thing = other_circle([2, 2], 20)
|
||||
"#;
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/optional_params.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -813,7 +832,7 @@ const part = roundedRectangle([0, 0], 20, 20, 4)
|
||||
|> extrude(2, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/rounded_with_holes.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -821,7 +840,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/top_level_expression.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -835,7 +854,7 @@ const part = startSketchOn('XY')
|
||||
|> extrude(1, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/patterns_linear_basic_with_math.png",
|
||||
&result,
|
||||
@ -851,7 +870,7 @@ async fn serial_test_patterns_linear_basic() {
|
||||
|> extrude(1, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -867,7 +886,7 @@ async fn serial_test_patterns_linear_basic_3d() {
|
||||
|> patternLinear3d({axis: [1, 0, 1], repetitions: 3, distance: 6}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic_3d.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -879,7 +898,7 @@ async fn serial_test_patterns_linear_basic_negative_distance() {
|
||||
|> extrude(1, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/patterns_linear_basic_negative_distance.png",
|
||||
&result,
|
||||
@ -895,7 +914,7 @@ async fn serial_test_patterns_linear_basic_negative_axis() {
|
||||
|> extrude(1, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/patterns_linear_basic_negative_axis.png",
|
||||
&result,
|
||||
@ -920,7 +939,7 @@ const rectangle = startSketchOn('XY')
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic_holes.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -932,7 +951,7 @@ async fn serial_test_patterns_circular_basic_2d() {
|
||||
|> extrude(1, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/patterns_circular_basic_2d.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -948,7 +967,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/patterns_circular_basic_3d.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -964,7 +983,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/patterns_circular_3d_tilted_axis.png",
|
||||
&result,
|
||||
@ -976,7 +995,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
@ -988,7 +1007,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/import_obj_with_mtl.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -996,7 +1015,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/import_obj_with_mtl_units.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -1004,7 +1023,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/import_gltf_with_bin.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -1012,7 +1031,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/import_gltf_embedded.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -1020,7 +1039,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/import_glb.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -1028,7 +1047,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/import_glb_no_assign.png", &result, 0.999);
|
||||
}
|
||||
|
||||
@ -1036,7 +1055,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 = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
@ -1061,7 +1080,7 @@ async fn serial_test_cube_mm() {
|
||||
const myCube = cube([0,0], 10)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/cube_mm.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1194,7 +1213,7 @@ const part002 = startSketchOn(part001, "here")
|
||||
|> extrude(1, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
@ -1235,7 +1254,7 @@ const part003 = startSketchOn(part002, "end")
|
||||
|> extrude(5, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_of_face.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1252,7 +1271,7 @@ async fn serial_test_stdlib_kcl_error_right_code_path() {
|
||||
|> extrude(2, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
@ -1280,7 +1299,7 @@ const part002 = startSketchOn(part001, "end")
|
||||
|> extrude(5, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_circle.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1304,7 +1323,7 @@ const part002 = startSketchOn(part001, "end")
|
||||
|> extrude(5, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_circle_tagged.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1346,7 +1365,7 @@ const part = rectShape([0, 0], 20, 20)
|
||||
}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
@ -1367,7 +1386,7 @@ async fn serial_test_big_number_angle_to_match_length_x() {
|
||||
|> extrude(10, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/big_number_angle_to_match_length_x.png",
|
||||
&result,
|
||||
@ -1388,7 +1407,7 @@ async fn serial_test_big_number_angle_to_match_length_y() {
|
||||
|> extrude(10, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image(
|
||||
"tests/executor/outputs/big_number_angle_to_match_length_y.png",
|
||||
&result,
|
||||
@ -1412,7 +1431,7 @@ async fn serial_test_simple_revolve() {
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1432,7 +1451,7 @@ async fn serial_test_simple_revolve_uppercase() {
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_uppercase.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1452,7 +1471,7 @@ async fn serial_test_simple_revolve_negative() {
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_negative.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1472,7 +1491,7 @@ async fn serial_test_revolve_bad_angle_low() {
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
@ -1497,7 +1516,7 @@ async fn serial_test_revolve_bad_angle_high() {
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
@ -1522,7 +1541,7 @@ async fn serial_test_simple_revolve_custom_angle() {
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_custom_angle.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1542,7 +1561,7 @@ async fn serial_test_simple_revolve_custom_axis() {
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_custom_axis.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1566,7 +1585,7 @@ const sketch001 = startSketchOn(box, "end")
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_edge.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1590,7 +1609,7 @@ const sketch001 = startSketchOn(box, "revolveAxis")
|
||||
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
@ -1617,7 +1636,7 @@ const sketch001 = startSketchOn(box, "END")
|
||||
}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face_circle_edge.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1639,7 +1658,7 @@ const sketch001 = startSketchOn(box, "END")
|
||||
}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face_circle.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1665,7 +1684,7 @@ const sketch001 = startSketchOn(box, "end")
|
||||
}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1679,7 +1698,7 @@ async fn serial_test_basic_revolve_circle() {
|
||||
}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/basic_revolve_circle.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1706,7 +1725,7 @@ const part002 = startSketchOn(part001, 'end')
|
||||
|> extrude(5, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_sketch_on_edge.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1767,7 +1786,7 @@ const plumbus1 = make_circle(p, 'b', [0, 0], 2.5)
|
||||
}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/plumbus_fillets.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1775,7 +1794,7 @@ const plumbus1 = make_circle(p, 'b', [0, 0], 2.5)
|
||||
async fn serial_test_empty_file_is_ok() {
|
||||
let code = r#""#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
@ -1805,7 +1824,7 @@ async fn serial_test_member_expression_in_params() {
|
||||
capScrew([0, 0.5, 0], 50, 37.5, 50, 25)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/member_expression_in_params.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1850,7 +1869,7 @@ const bracket = startSketchOn('XY')
|
||||
}, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
@ -1874,7 +1893,7 @@ const secondSketch = startSketchOn(part001, '')
|
||||
|> extrude(20, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
@ -1905,7 +1924,7 @@ const extrusion = startSketchOn('XY')
|
||||
|> extrude(height, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await;
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await;
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
@ -1923,7 +1942,7 @@ async fn serial_test_xz_plane() {
|
||||
|> extrude(5 + 7, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/xz_plane.png", &result, 1.0);
|
||||
}
|
||||
|
||||
@ -1937,38 +1956,56 @@ async fn serial_test_neg_xz_plane() {
|
||||
|> extrude(5 + 7, %)
|
||||
"#;
|
||||
|
||||
let result = snapshot_on_test_server(code.to_owned(), test_name!()).await.unwrap();
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).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}");
|
||||
}
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn serial_test_linear_pattern3d_a_pattern() {
|
||||
let code = r#"const exampleSketch = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 2], %)
|
||||
|> line([3, 1], %)
|
||||
|> line([0, -4], %)
|
||||
|> close(%)
|
||||
|> extrude(1, %)
|
||||
|
||||
const pattn1 = patternLinear3d({
|
||||
axis: [1, 0, 0],
|
||||
repetitions: 6,
|
||||
distance: 6
|
||||
}, exampleSketch)
|
||||
|
||||
const pattn2 = patternLinear3d({
|
||||
axis: [0, 0, 1],
|
||||
distance: 1,
|
||||
repetitions: 6
|
||||
}, pattn1)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/linear_pattern3d_a_pattern.png", &result, 1.0);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn serial_test_circular_pattern3d_a_pattern() {
|
||||
let code = r#"const exampleSketch = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 2], %)
|
||||
|> line([3, 1], %)
|
||||
|> line([0, -4], %)
|
||||
|> close(%)
|
||||
|> extrude(1, %)
|
||||
|
||||
const pattn1 = patternLinear3d({
|
||||
axis: [1, 0, 0],
|
||||
repetitions: 6,
|
||||
distance: 6
|
||||
}, exampleSketch)
|
||||
|
||||
const pattn2 = patternCircular3d({axis: [0,0, 1], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, pattn1)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/circular_pattern3d_a_pattern.png", &result, 1.0);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 127 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: 124 KiB After Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 289 KiB After Width: | Height: | Size: 250 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 209 KiB |
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 214 KiB |
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 212 KiB |
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 225 KiB |
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 173 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 176 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: 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: 162 KiB After Width: | Height: | Size: 147 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 198 KiB |
After Width: | Height: | Size: 176 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 125 KiB |
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 111 KiB |
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 132 KiB |
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 141 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 167 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 168 KiB |
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 117 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: 181 KiB After Width: | Height: | Size: 173 KiB |
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 173 KiB |
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 147 KiB |
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 195 KiB |
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 148 KiB |
Before Width: | Height: | Size: 165 KiB After Width: | Height: | Size: 163 KiB |
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 104 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: 152 KiB After Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 116 KiB |