mod actor;
mod engine;
pub use engine::{BlockHandle, Messages, SingleThreadedEngine};
#[cfg(not(target_arch = "wasm32"))]
#[cfg(test)]
mod tests {
use std::{thread, time::Duration};
use crate::base;
use crate::base::program::{
Program, ProgramBlock,
data::{LinkData, PinValue, Position},
};
use crate::blocks::{math::Add, misc::SineWave};
use base::block::{BlockConnect, BlockProps};
use base::engine::messages::EngineMessage::{
GetCurrentProgramReq, GetCurrentProgramRes, InspectBlockReq, InspectBlockRes,
LoadProgramReq, LoadProgramRes, Shutdown,
};
use super::SingleThreadedEngine;
use base::engine::Engine;
use tokio::{runtime::Runtime, sync::mpsc, time::sleep};
use uuid::Uuid;
#[tokio::test(flavor = "current_thread")]
async fn engine_test() {
use crate::base::block::connect::connect_output;
let mut add1 = Add::new();
let add_uuid = *add1.id();
let mut sine1 = SineWave::new();
sine1.amplitude.val = Some(3.into());
sine1.freq.val = Some(200.into());
connect_output(&mut sine1.out, add1.inputs_mut()[0]).expect("Connected");
let mut sine2 = SineWave::new();
sine2.amplitude.val = Some(7.into());
sine2.freq.val = Some(400.into());
sine2
.connect_output("out", add1.inputs_mut()[1])
.expect("Connected");
let mut eng = SingleThreadedEngine::new();
let (sender, mut receiver) = mpsc::channel(32);
let channel_id = Uuid::new_v4();
let engine_sender = eng.create_message_channel(channel_id, sender.clone());
thread::spawn(move || {
let rt = Runtime::new().expect("RT");
let handle = rt.spawn(async move {
sleep(Duration::from_millis(300)).await;
let _ = engine_sender
.send(InspectBlockReq(channel_id, add_uuid))
.await;
let res = receiver.recv().await;
if let Some(InspectBlockRes(Ok(data))) = res {
assert_eq!(data.id, add_uuid.to_string());
assert_eq!(data.name, "Add");
assert_eq!(data.inputs.len(), 16);
assert_eq!(data.outputs.len(), 1);
} else {
panic!("Failed to find block: {:?}", res)
}
let _ = engine_sender.send(Shutdown).await;
});
rt.block_on(handle)
});
eng.schedule(add1);
eng.schedule(sine1);
eng.schedule(sine2);
eng.run().await;
}
#[tokio::test(flavor = "current_thread")]
async fn program_load_save_round_trip() {
let add0_uuid = Uuid::new_v4();
let add1_uuid = Uuid::new_v4();
let link_uuid = Uuid::new_v4();
let mut input_consts = std::collections::BTreeMap::new();
input_consts.insert(
"in0".to_string(),
PinValue {
value: 42.into(),
is_connected: false,
},
);
let mut blocks = std::collections::BTreeMap::new();
blocks.insert(
add0_uuid.to_string(),
ProgramBlock {
name: "Add".to_string(),
lib: "core".to_string(),
label: Some("Summer A".to_string()),
positions: Some(Position { x: 10.0, y: 20.0 }),
inputs: input_consts,
outputs: Default::default(),
},
);
blocks.insert(
add1_uuid.to_string(),
ProgramBlock {
name: "Add".to_string(),
lib: "core".to_string(),
label: Some("Summer B".to_string()),
positions: Some(Position { x: 200.0, y: 20.0 }),
inputs: Default::default(),
outputs: Default::default(),
},
);
let mut links = std::collections::BTreeMap::new();
links.insert(
link_uuid.to_string(),
LinkData {
id: Some(link_uuid.to_string()),
source_block_uuid: add0_uuid.to_string(),
target_block_uuid: add1_uuid.to_string(),
source_block_pin_name: "out".to_string(),
target_block_pin_name: "in0".to_string(),
},
);
let program = Program {
name: Some("rust-headless-test".to_string()),
description: None,
blocks,
links,
};
let mut eng = SingleThreadedEngine::new();
let (sender, mut receiver) = mpsc::channel(32);
let channel_id = Uuid::new_v4();
let engine_sender = eng.create_message_channel(channel_id, sender.clone());
thread::spawn(move || {
let rt = Runtime::new().expect("RT");
let handle = rt.spawn(async move {
sleep(Duration::from_millis(100)).await;
let _ = engine_sender
.send(LoadProgramReq(channel_id, program.clone()))
.await;
match receiver.recv().await {
Some(LoadProgramRes(Ok(()))) => {}
other => panic!("Expected LoadProgramRes(Ok), got {:?}", other),
}
let _ = engine_sender.send(GetCurrentProgramReq(channel_id)).await;
let res = receiver.recv().await;
match res {
Some(GetCurrentProgramRes(Ok(saved))) => {
assert_eq!(saved.blocks.len(), 2);
let saved0 = saved
.blocks
.get(&add0_uuid.to_string())
.expect("first block round-trips");
assert_eq!(saved0.name, "Add");
assert_eq!(saved0.label.as_deref(), Some("Summer A"));
let pos = saved0.positions.expect("position survives");
assert!((pos.x - 10.0).abs() < f64::EPSILON);
let in0 = saved0.inputs.get("in0").expect("in0 survives");
assert_eq!(in0.value, 42.into());
assert_eq!(saved.links.len(), 1);
let saved1 = saved
.blocks
.get(&add1_uuid.to_string())
.expect("second block round-trips");
let target_in0 = saved1.inputs.get("in0").expect("target input present");
assert!(
target_in0.is_connected,
"wired target input should report is_connected=true"
);
}
other => panic!("Expected GetCurrentProgramRes(Ok), got {:?}", other),
}
let _ = engine_sender.send(Shutdown).await;
});
rt.block_on(handle)
});
eng.run().await;
}
}