Create simple test framework for Act implementors
This commit is contained in:
parent
83135b3045
commit
a52309484e
4 changed files with 230 additions and 0 deletions
|
@ -155,3 +155,151 @@ impl<'a, X: SeqAct<'a, BuildParam=()>> Act<'a> for SeqActor<'a, X> {
|
||||||
self.seq_act.run(self.param)
|
self.seq_act.run(self.param)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub struct SeqActTestHarness<'a, ParamT, ConfigT, ActorT, FnT>
|
||||||
|
where
|
||||||
|
ConfigT: 'a,
|
||||||
|
ActorT: SeqAct<'a, BuildParam=ParamT, Config=ConfigT>,
|
||||||
|
FnT: Fn(&'a ConfigT, ParamT) -> Result<ActorT, ActError>,
|
||||||
|
{
|
||||||
|
actor_factory: FnT,
|
||||||
|
inputs: Vec<(&'a ConfigT, ParamT, Primitive)>,
|
||||||
|
outputs: std::collections::VecDeque<Expected>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Expected {
|
||||||
|
Output(Primitive),
|
||||||
|
BuildErr(ActError),
|
||||||
|
OutputIdc,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<'a, ParamT, ConfigT, ActorT, FnT> SeqActTestHarness<'a, ParamT, ConfigT, ActorT, FnT>
|
||||||
|
where
|
||||||
|
ConfigT: 'a,
|
||||||
|
ActorT: SeqAct<'a, BuildParam=ParamT, Config=ConfigT>,
|
||||||
|
FnT: Fn(&'a ConfigT, ParamT) -> Result<ActorT, ActError>,
|
||||||
|
{
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn new(factory: FnT, inputs_vars: Vec<(&'a ConfigT, ParamT, Primitive)>, output_vars: std::collections::VecDeque<Expected>) -> Self {
|
||||||
|
Self {
|
||||||
|
actor_factory: factory,
|
||||||
|
inputs: inputs_vars,
|
||||||
|
outputs: output_vars,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn builder(factory: FnT) -> Self {
|
||||||
|
Self {
|
||||||
|
actor_factory: factory,
|
||||||
|
inputs: Vec::new(),
|
||||||
|
outputs: std::collections::VecDeque::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn with_input(mut self, conf: &'a ConfigT, build_param: ParamT, run_param: Primitive) -> Self {
|
||||||
|
self.inputs.push((conf, build_param, run_param));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn expect(mut self, expected: Expected) -> Self {
|
||||||
|
self.outputs.push_back(expected);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_io(mut self, input: (&'a ConfigT, ParamT, Primitive), output: Expected) -> Self {
|
||||||
|
self.inputs.push(input);
|
||||||
|
self.outputs.push_back(output);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(mut self) {
|
||||||
|
for input in self.inputs {
|
||||||
|
let expected = self.outputs.pop_front().expect("Not enough outputs for available inputs");
|
||||||
|
let actor_result = (self.actor_factory)(input.0, input.1);
|
||||||
|
match expected {
|
||||||
|
Expected::Output(primitive) => {
|
||||||
|
let debug_prim = crate::runtime::primitive_utils::debug(&input.2);
|
||||||
|
let actual = actor_result.expect("Expected Ok actor").run(input.2);
|
||||||
|
let debug_actual = crate::runtime::primitive_utils::debug(&actual);
|
||||||
|
match primitive {
|
||||||
|
Primitive::Empty => if let Primitive::Empty = actual {
|
||||||
|
// good!
|
||||||
|
} else {
|
||||||
|
panic!("Expected SeqAct.run({}) to output Empty, got `{}`", debug_prim, debug_actual);
|
||||||
|
},
|
||||||
|
Primitive::String(s) => if let Primitive::String(actual) = actual {
|
||||||
|
assert_eq!(actual, s, "Expected SeqAct.run({}) to output Primitive::String(`{}`)", debug_prim, s);
|
||||||
|
} else {
|
||||||
|
panic!("Expected SeqAct.run({}) to output Primitive::String(`{}`), got `{}`", debug_prim, s, debug_actual);
|
||||||
|
},
|
||||||
|
Primitive::Json(j) => if let Primitive::Json(actual) = actual {
|
||||||
|
assert_eq!(actual, j, "Expected SeqAct.run({}) to output Primitive::Json(`{}`)", debug_prim, j);
|
||||||
|
} else {
|
||||||
|
panic!("Expected SeqAct.run({}) to output Primitive::Json(`{}`), got `{}`", debug_prim, j, debug_actual);
|
||||||
|
},
|
||||||
|
_ => todo!("NGNIUS!!! Complete your damn test harness!!"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Expected::BuildErr(err) => {
|
||||||
|
if let Err(actual) = actor_result {
|
||||||
|
assert_eq!(actual, err, "Expected and actual build error do not match");
|
||||||
|
} else {
|
||||||
|
panic!("Expected build error `{}`, but result was ok", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Expected::OutputIdc => {
|
||||||
|
actor_result.expect("Expected Ok actor").run(input.2);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::config::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn full_actor_test() {
|
||||||
|
let (runtime_io, _result_rx, _js_rx) = crate::runtime::RuntimeIO::mock();
|
||||||
|
SeqActTestHarness::builder(Actor::build)
|
||||||
|
.with_io(
|
||||||
|
(&ElementConfig::Button(ButtonConfig {
|
||||||
|
title: "Test Button".into(),
|
||||||
|
on_click: TopLevelActionConfig::Mirror(MirrorAction)
|
||||||
|
}),
|
||||||
|
(0, &runtime_io),
|
||||||
|
Primitive::Empty),
|
||||||
|
|
||||||
|
Expected::Output(Primitive::Empty))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn top_level_actor_test() {
|
||||||
|
let (runtime_io, _result_rx, _js_rx) = crate::runtime::RuntimeIO::mock();
|
||||||
|
SeqActTestHarness::builder(TopLevelActorType::build)
|
||||||
|
.with_io(
|
||||||
|
(&TopLevelActionConfig::Mirror(MirrorAction), &runtime_io, Primitive::Empty), Expected::Output(Primitive::Empty))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn std_actor_test() {
|
||||||
|
let (runtime_io, _result_rx, _js_rx) = crate::runtime::RuntimeIO::mock();
|
||||||
|
SeqActTestHarness::builder(ActorType::build)
|
||||||
|
.with_io(
|
||||||
|
(&ActionConfig::Transform(TransformAction{
|
||||||
|
transformer: TransformTypeAction::Log(LogTransformAction{level: LogLevel::DEBUG})}),
|
||||||
|
&runtime_io,
|
||||||
|
Primitive::String("Test log output".into())),
|
||||||
|
|
||||||
|
Expected::OutputIdc)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -75,3 +75,67 @@ impl<'a> SeqAct<'a> for JsonActor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::runtime::actors::*;
|
||||||
|
use crate::config::*;
|
||||||
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn json_actor_test() {
|
||||||
|
//let (runtime_io, _result_rx, _js_rx) = crate::runtime::RuntimeIO::mock();
|
||||||
|
SeqActTestHarness::builder(JsonActor::build)
|
||||||
|
// test 1 ---
|
||||||
|
.with_io(
|
||||||
|
(&JsonAction {
|
||||||
|
jmespath: r"locations[?state == 'WA'].name | sort(@) | {WashingtonCities: join(', ', @)}".into(),
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
Primitive::Json(
|
||||||
|
r#"{
|
||||||
|
"locations": [
|
||||||
|
{"name": "Seattle", "state": "WA"},
|
||||||
|
{"name": "New York", "state": "NY"},
|
||||||
|
{"name": "Bellevue", "state": "WA"},
|
||||||
|
{"name": "Olympia", "state": "WA"}
|
||||||
|
]
|
||||||
|
}"#.into()
|
||||||
|
)),
|
||||||
|
|
||||||
|
Expected::Output(Primitive::Json(
|
||||||
|
r#"{"WashingtonCities":"Bellevue, Olympia, Seattle"}"#.into()
|
||||||
|
)))
|
||||||
|
|
||||||
|
// test 2 ---
|
||||||
|
.with_io(
|
||||||
|
(&JsonAction {
|
||||||
|
jmespath: r"locations[?state == 'WA'].name | sort(@) | {WashingtonCities: join(', ', @)}".into(),
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
Primitive::Bool(false)
|
||||||
|
),
|
||||||
|
|
||||||
|
Expected::Output(Primitive::Empty))
|
||||||
|
|
||||||
|
// test 3 ---
|
||||||
|
.with_io(
|
||||||
|
(&JsonAction {
|
||||||
|
jmespath: "garb@ge".into(),
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
Primitive::Json(
|
||||||
|
r#"{
|
||||||
|
"locations": [
|
||||||
|
{"name": "Seattle", "state": "WA"},
|
||||||
|
{"name": "New York", "state": "NY"},
|
||||||
|
{"name": "Bellevue", "state": "WA"},
|
||||||
|
{"name": "Olympia", "state": "WA"}
|
||||||
|
]
|
||||||
|
}"#.into()
|
||||||
|
)),
|
||||||
|
|
||||||
|
Expected::BuildErr("Failed to compile jmespath `garb@ge`: Parse error: Did not parse the complete expression -- found At (line 0, column 4)\ngarb@ge\n ^\n".into()))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ mod sequential_actor;
|
||||||
mod transform_actor;
|
mod transform_actor;
|
||||||
|
|
||||||
pub use actor::{Actor, Act, ActError, ActorType, SeqAct, SeqActor, TopLevelActorType};
|
pub use actor::{Actor, Act, ActError, ActorType, SeqAct, SeqActor, TopLevelActorType};
|
||||||
|
#[cfg(test)]
|
||||||
|
pub use actor::{Expected, SeqActTestHarness, /*ActTestHarness*/};
|
||||||
pub use command_actor::CommandActor;
|
pub use command_actor::CommandActor;
|
||||||
pub use javascript_actor::JavascriptActor;
|
pub use javascript_actor::JavascriptActor;
|
||||||
pub use json_actor::JsonActor;
|
pub use json_actor::JsonActor;
|
||||||
|
|
|
@ -12,6 +12,22 @@ pub struct RuntimeIO {
|
||||||
pub javascript: Sender<JavascriptCommand>,
|
pub javascript: Sender<JavascriptCommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl RuntimeIO {
|
||||||
|
pub fn mock() -> (Self, Receiver<RouterCommand>, Receiver<JavascriptCommand>) {
|
||||||
|
let (s1, r1) = mpsc::channel();
|
||||||
|
let (s2, r2) = mpsc::channel();
|
||||||
|
(
|
||||||
|
Self {
|
||||||
|
result: s1,
|
||||||
|
javascript: s2,
|
||||||
|
},
|
||||||
|
r1,
|
||||||
|
r2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RuntimeExecutor {
|
pub struct RuntimeExecutor {
|
||||||
config_data: BaseConfig,
|
config_data: BaseConfig,
|
||||||
tasks_receiver: Receiver<QueueItem>,
|
tasks_receiver: Receiver<QueueItem>,
|
||||||
|
|
Reference in a new issue