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)
|
||||
}
|
||||
}
|
||||
|
||||
#[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;
|
||||
|
||||
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 javascript_actor::JavascriptActor;
|
||||
pub use json_actor::JsonActor;
|
||||
|
|
|
@ -12,6 +12,22 @@ pub struct RuntimeIO {
|
|||
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 {
|
||||
config_data: BaseConfig,
|
||||
tasks_receiver: Receiver<QueueItem>,
|
||||
|
|
Reference in a new issue