use boomerang::{builder::prelude::*, runtime, Reaction, Reactor}; #[derive(Default)] struct GeneratedDelayState { y_state: u32, } /// Test logical action with delay. #[derive(Reactor, Clone)] #[reactor(state = GeneratedDelayState, reaction = "ReactionYIn", reaction = "ReactionAct")] struct GeneratedDelay { y_in: TypedPortKey, y_out: TypedPortKey, #[reactor(action(min_delay = "100 msec"))] act: TypedActionKey, } #[derive(Reaction)] #[reaction(reactor = "GeneratedDelay")] struct ReactionYIn<'a> { y_in: runtime::InputRef<'a, u32>, #[reaction(effects)] act: runtime::ActionRef<'a>, } impl Trigger for ReactionYIn<'_> { fn trigger( mut self, ctx: &mut runtime::Context, state: &mut ::State, ) { state.y_state = self.y_in.unwrap(); ctx.schedule_action(&mut self.act, None, None); } } #[derive(Reaction)] #[reaction(reactor = "GeneratedDelay", triggers(action = "act"))] struct ReactionAct<'a> { y_out: runtime::OutputRef<'a, u32>, } impl Trigger for ReactionAct<'_> { fn trigger( mut self, _ctx: &mut runtime::Context, state: &mut ::State, ) { *self.y_out = Some(state.y_state); } } #[derive(Reactor, Clone)] #[reactor(state = "()", reaction = "SourceReactionStartup")] struct SourceBuilder { out: TypedPortKey, } #[derive(Reaction)] #[reaction(reactor = "SourceBuilder", triggers(startup))] struct SourceReactionStartup<'a> { out: runtime::OutputRef<'a, u32>, } impl Trigger for SourceReactionStartup<'_> { fn trigger( mut self, _ctx: &mut runtime::Context, _state: &mut ::State, ) { *self.out = Some(1); } } #[derive(Reactor, Clone)] #[reactor(state = "bool", reaction = "SinkReactionIn")] struct Sink { inp: TypedPortKey, } #[derive(Reaction)] #[reaction(reactor = "Sink")] struct SinkReactionIn<'a> { #[reaction(path = inp)] _inp: runtime::InputRef<'a, u32>, } impl Trigger for SinkReactionIn<'_> { fn trigger(self, ctx: &mut runtime::Context, state: &mut ::State) { let elapsed_logical = ctx.get_elapsed_logical_time(); let logical = ctx.get_logical_time(); let physical = ctx.get_physical_time(); println!("logical time: {:?}", logical); println!("physical time: {:?}", physical); println!("elapsed logical time: {:?}", elapsed_logical); assert!( elapsed_logical == runtime::Duration::from_millis(100), "ERROR: Expected 100 msecs but got {:?}", elapsed_logical ); println!("SUCCESS. Elapsed logical time is 100 msec."); *state = true; } } #[derive(Reactor, Clone)] #[reactor( state = "()", connection(from = "source.out", to = "g.y_in"), connection(from = "g.y_out", to = "sink.inp") )] #[allow(dead_code)] struct ActionDelayBuilder { #[reactor(child = ())] source: SourceBuilder, #[reactor(child = false)] sink: Sink, #[reactor(child = GeneratedDelayState::default())] g: GeneratedDelay, } #[test] fn action_delay() { tracing_subscriber::fmt::init(); let (_, sched) = boomerang_util::runner::build_and_test_reactor::( "action_delay", (), true, false, ) .unwrap(); let env = sched.into_env(); let sink_state = env .find_reactor_by_name("sink") .and_then(|reactor| reactor.get_state::()) .unwrap(); assert!(sink_state, "SinkReactionIn did not trigger"); }