def test_default_transition_from_finished_composite_state(self): statechart = Statechart(name='statechart') statechart_init = InitialState(statechart) composite_state = CompositeState(name='composite', context=statechart) comp_init = InitialState(composite_state) a = State(name='a', context=composite_state) comp_final = FinalState(composite_state) Transition(start=statechart_init, end=composite_state) Transition(start=comp_init, end=a) Transition(start=a, end=comp_final, event=Event('e')) b = State(name='b', context=statechart) c = State(name='c', context=statechart) d = State(name='d', context=statechart) Transition(start=composite_state, end=c, event=Event('f')) Transition(start=composite_state, end=b, event=Event('e')) Transition(start=composite_state, end=d) statechart.start() assert statechart.is_active('a') statechart.dispatch(Event('e')) assert statechart.is_active('d')
def test_transition_event_consumed(self, empty_statechart): sc = empty_statechart init = InitialState(sc) # a = State(name='a', context=sc) b = State(name='b', context=sc) cs = CompositeState(name='cs', context=sc) cs_init = InitialState(cs) cs_a = State(name='cs a', context=cs) cs_b = State(name='cs b', context=cs) Transition(start=init, end=cs) Transition(start=cs, end=cs_init) Transition(start=cs_init, end=cs_a) Transition(start=cs_a, end=cs_b, event='home') Transition(start=cs, end=b, event='home') sc.start() assert sc.is_active('cs a') sc.dispatch(Event('home')) assert sc.is_active('cs b')
def test_composite_statechart_finished(self): statechart = Statechart(name='statechart') init = InitialState(statechart) final = FinalState(statechart) composite = CompositeState(name='composite', context=statechart) composite_init = InitialState(composite) composite_default = State(name='composite_default', context=composite) composite_final = FinalState(composite) finish = Event('finish') Transition(start=init, end=composite) Transition(start=composite_init, end=composite_default) Transition(start=composite_default, end=composite_final, event=finish) Transition(start=composite, end=final) statechart.start() assert statechart.is_active('composite') assert statechart.is_active('composite_default') assert not statechart.is_finished() statechart.dispatch(finish) assert statechart.is_finished()
def test_transition_hierarchy(self, empty_statechart): sc = empty_statechart init = InitialState(sc) top = CompositeState(name='top', context=sc) top_init = InitialState(top) middle_a = CompositeState(name='middle_a', context=top) middle_b = CompositeState(name='middle_b', context=top) middle_a_init = InitialState(middle_a) bottom_a1 = State(name='bottom_a1', context=middle_a) bottom_a2 = State(name='bottom_a2', context=middle_a) middle_b_init = InitialState(middle_b) bottom_b1 = State(name='bottom_b1', context=middle_b) # Setup default transitions Transition(start=init, end=top) Transition(start=top_init, end=middle_a) Transition(start=middle_a_init, end=bottom_a1) Transition(start=middle_b_init, end=bottom_b1) # Setup event triggers across = Event('across') up = Event('up') # Setup external transitions Transition(start=bottom_a1, end=bottom_a2, event=across) Transition(start=bottom_a2, end=middle_b, event=up) Transition(start=middle_b, end=middle_a, event=across) sc.start() assert sc.is_active('top') assert sc.is_active('middle_a') assert sc.is_active('bottom_a1') sc.dispatch(across) assert sc.is_active('top') assert sc.is_active('middle_a') assert sc.is_active('bottom_a2') sc.dispatch(up) assert sc.is_active('top') assert sc.is_active('middle_b') assert sc.is_active('bottom_b1') sc.dispatch(across) assert sc.is_active('top') assert sc.is_active('middle_a') assert sc.is_active('bottom_a1')
def test_transition_from_initial_state_with_event_trigger(self): startchart = Statechart(name='statechart') initial_state = InitialState(startchart) default_state = State(name='default', context=startchart) with pytest.raises(RuntimeError): Transition(start=initial_state, end=default_state, event=Event('event'))
def test_execute(self, empty_statechart): initial_state = InitialState(empty_statechart) default_state = self.StateSpy(name='next', context=empty_statechart) Transition(start=initial_state, end=default_state) internal_event = Event(name='internal-event') internal_action = ActionSpy() InternalTransition(state=default_state, event=internal_event, action=internal_action) empty_statechart.start() assert empty_statechart.is_active('next') assert default_state.entry_executed is True assert default_state.do_executed is True assert default_state.exit_executed is False # Ensure we don't leave and re-enter the default state after triggering # the internal transition. default_state.entry_executed = False default_state.do_executed = False empty_statechart.dispatch(internal_event) assert default_state.entry_executed is False assert default_state.do_executed is False assert default_state.exit_executed is False assert internal_action.executed is True
def test_submachines(self): statechart = Statechart(name='statechart') init = InitialState(statechart) top_a = self.Submachine('top a', statechart) top_b = self.Submachine('top b', statechart) top_a_to_b = Event('top ab') Transition(start=init, end=top_a) Transition(start=top_a, end=top_b, event=top_a_to_b) statechart.start() assert statechart.is_active('top a') assert statechart.is_active('sub state a') statechart.dispatch(top_a.sub_a_to_b) assert statechart.is_active('top a') assert statechart.is_active('sub state b') statechart.dispatch(top_a_to_b) assert statechart.is_active('top b') assert statechart.is_active('sub state a') statechart.dispatch(top_a.sub_a_to_b) assert statechart.is_active('top b') assert statechart.is_active('sub state b')
def state(): statechart = Statechart(name='statechart') initial_state = InitialState(statechart) next_state = State(name='next', context=statechart) Transition(initial_state, next_state) statechart.start() return next_state
def concurrent_statechart(): def composite(name, context): composite = CompositeState(name=name, context=context) composite.init = InitialState(composite) composite.state = State(name='state', context=composite) composite.final = FinalState(composite) Transition(start=composite.init, end=composite.state) Transition(start=composite.state, end=composite.final, event=Event('finish')) return composite sc = Statechart('simple') sc.init = InitialState(sc) sc.concurrent = ConcurrentState(name='compound', context=sc) sc.concurrent.composite_a = composite(name='a', context=sc.concurrent) sc.concurrent.composite_b = composite(name='b', context=sc.concurrent) sc.concurrent.composite_c = composite(name='c', context=sc.concurrent) sc.final = FinalState(sc) Transition(start=sc.init, end=sc.concurrent) Transition(start=sc.concurrent, end=sc.final, event=Event('finish')) return sc
def main() -> None: """Code here shows how everything is wired together. The following lines are the expected output: state 'state-a' got event 'None' state 'state-b' got event 'Event(name="to-state-b", data={})' """ # Create the state machine -- the root context of every workflow. state_machine = Statechart("state-machine") # Create the workflow metadata with all the data accessible to all states and workflows. metadata = WorkflowMetadata(a=10, b="foo", c="bar") # Create the workflow factory backed by the workflow metadata. factory = WorkflowFactory(metadata) # Create the main workflow using the workflow factory. main_workflow = factory.create_workflow("main-workflow", state_machine, MainWorkflow) # Set up the main transitions. init = InitialState(state_machine) final = FinalState(state_machine) Transition(init, main_workflow) Transition(main_workflow, final, event=Event("final")) # Run the state machine. state_machine.start() state_machine.dispatch(Event("to-state-b"))
def test_choice_state_transitions(self, choice, expected_state_name): class MyMetadata(Metadata): def __init__(self): super().__init__() self.value = None class IsA(Guard): def check(self, metadata, event): return metadata.value == 'a' myMetadata = MyMetadata() myMetadata.value = choice statechart = Statechart(name='statechart', metadata=myMetadata) init = InitialState(statechart) state_a = State(name='a', context=statechart) state_b = State(name='b', context=statechart) choice = ChoiceState(context=statechart) Transition(start=init, end=choice) Transition(start=choice, end=state_a, event=None, guard=IsA()) Transition(start=choice, end=state_b, event=None, guard=ElseGuard()) statechart.start() assert statechart.is_active(expected_state_name)
def test_add_transition(self): statechart = Statechart(name='statechart') initial_state = InitialState(statechart) default_state = State(name='default', context=statechart) default_transition = Transition(start=initial_state, end=default_state) assert default_transition in initial_state._transitions
def test_multiple_transitions_from_initial_state(self): startchart = Statechart(name='statechart') initial_state = InitialState(startchart) default_state = State(name='default', context=startchart) Transition(start=initial_state, end=default_state) with pytest.raises(RuntimeError): Transition(start=initial_state, end=default_state)
def test_active_states(self): statechart = Statechart(name='a') statechart_init = InitialState(statechart) b = CompositeState(name='b', context=statechart) b_init = InitialState(b) c = CompositeState(name='c', context=b) c_init = InitialState(c) d = State(name='d', context=c) Transition(start=statechart_init, end=b) Transition(start=b_init, end=c) Transition(start=c_init, end=d) statechart.start() assert statechart.active_states() == [statechart, b, c, d]
def test_activate_initial_state(self): startchart = Statechart(name='statechart') initial_state = InitialState(startchart) default_state = State(name='default', context=startchart) Transition(start=initial_state, end=default_state) startchart.start() initial_state.activate(metadata=startchart.metadata, event=None) assert startchart.is_active('default')
def simple_statechart(): sc = Statechart('simple') sc.init = InitialState(sc) sc.state = State(name='state', context=sc) sc.final = FinalState(sc) Transition(start=sc.init, end=sc.state) Transition(start=sc.state, end=sc.final, event=Event('finish')) return sc
def init(self, factory: WorkflowFactory) -> None: state_a = factory.create_state("state-a", self, StateA) state_b = factory.create_state("state-b", self, StateB) init = InitialState(self) final = FinalState(self) Transition(init, state_a) Transition(state_a, state_b, event=Event("to-state-b")) Transition(state_b, final, event=Event("final"))
def test_transition_from_initial_state_with_guard_condition(self): class MyGuard(Guard): def check(self, metadata, event): return False startchart = Statechart(name='statechart') initial_state = InitialState(startchart) default_state = State(name='default', context=startchart) with pytest.raises(RuntimeError): Transition(start=initial_state, end=default_state, event=None, guard=MyGuard())
def test_top_level_internal_transition(self, empty_statechart): sc = empty_statechart sc_init = InitialState(sc) cs = CompositeState(name='cs', context=sc) cs_init = InitialState(cs) cs_default = State(name='cs_default', context=cs) Transition(start=sc_init, end=cs) Transition(start=cs_init, end=cs_default) test_event = Event('internal-event-trigger') InternalTransition(state=cs, event=test_event) sc.start() assert sc.is_active('cs_default') sc.dispatch(test_event) assert sc.is_active('cs_default')
def __init__(self, name, context): CompositeState.__init__(self, name=name, context=context) init = InitialState(self) self.state_a = State(name='sub state a', context=self) self.state_b = State(name='sub state b', context=self) self.sub_a_to_b = Event('sub_ab') Transition(start=init, end=self.state_a) Transition(start=self.state_a, end=self.state_b, event=self.sub_a_to_b)
def __init__(self, name, context): super().__init__(name=name, context=context) # Count state entries and exit self.entries = 0 self.exits = 0 init = InitialState(self) self.default = State(name='default', context=self) self.local = State(name='local', context=self) Transition(start=init, end=self.default)
def composite(name, context): composite = CompositeState(name=name, context=context) composite.init = InitialState(composite) composite.state = State(name='state', context=composite) composite.final = FinalState(composite) Transition(start=composite.init, end=composite.state) Transition(start=composite.state, end=composite.final, event=Event('finish')) return composite
def test_create_transition(self, empty_statechart): initial_state = InitialState(empty_statechart) next_state = State(name='next', context=empty_statechart) transition = Transition(start=initial_state, end=next_state) # The transition should be added to the initial state's list of # outgoing transitions assert transition in initial_state._transitions # When executed, the transition should be setup to deactivate the # initial state and to activate the next state assert initial_state in transition.deactivate assert next_state in transition.activate
def test_transition_from_initial_state_with_guard_condition(self): startchart = Statechart(name='statechart') initial_state = InitialState(startchart) default_state = State(name='default', context=startchart) def my_guard(**kwargs): return False with pytest.raises(RuntimeError): Transition(start=initial_state, end=default_state, event=None, guard=my_guard)
def test_default_transition_isnt_executed_from_unfinished_composite_state( self): statechart = Statechart(name='statechart') statechart_init = InitialState(statechart) composite_state = CompositeState(name='composite', context=statechart) comp_init = InitialState(composite_state) a = State(name='a', context=composite_state) Transition(start=statechart_init, end=composite_state) Transition(start=comp_init, end=a) b = State(name='b', context=statechart) Transition(start=composite_state, end=b) statechart.start() assert statechart.is_active('a') statechart.dispatch(Event('e')) assert statechart.is_active('a')
def test_light_switch(self): """ --Flick--> init ---> Off On <--Flick-- Entry: Light = ON Exit: Light = OFF Internal: Flick: Count++ """ class On(State): def __init__(self, name, context, data): State.__init__(self, name=name, context=context) self.data = data def entry(self, event): self.data['light'] = 'on' def exit(self, event): self.data['light'] = 'off' def handle_internal(self, event): if event.name == 'flick': self.data['on_count'] += 1 sm = Statechart(name='sm') data = dict(light='off', on_count=0) sm.initial_state = InitialState(context=sm) off = State(name='off', context=sm) on = On(name='on', context=sm, data=data) Transition(start=sm.initial_state, end=off) Transition(start=off, end=on, event=Event('flick')) Transition(start=on, end=off, event=Event('flick')) sm.start() assert data['light'] == 'off' sm.dispatch(Event('flick')) assert data['light'] == 'on' assert data['on_count'] == 0 sm.dispatch(Event('flick')) assert data['light'] == 'off' assert data['on_count'] == 1
def test_simple_statechart_finished(self): statechart = Statechart(name='statechart') init = InitialState(statechart) default = State(name='default', context=statechart) final = FinalState(statechart) finish = Event('finish') Transition(start=init, end=default) Transition(start=default, end=final, event=finish) statechart.start() assert statechart.is_active('default') assert not statechart.is_finished() statechart.dispatch(finish) assert statechart.is_finished()
def test_transition_action_function_with_event(self, empty_statechart): self.state = False def set_state(event): self.state = event.data['state'] sc = empty_statechart initial = InitialState(sc) default = State(name='default', context=sc) next = State(name='next', context=sc) Transition(start=initial, end=default) Transition(start=default, end=next, event='next', action=set_state) sc.start() sc.dispatch(Event(name='next', data={'state': True})) assert self.state
def test_external_transition(self, empty_statechart): init = InitialState(empty_statechart) state_spy = self.StateSpy(name='spy', context=empty_statechart) Transition(start=init, end=state_spy) Transition(start=state_spy, end=state_spy, event='extern') empty_statechart.start() assert empty_statechart.is_active('spy') assert state_spy.entries is 1 assert state_spy.exits is 0 empty_statechart.dispatch(Event('extern')) # After dispatching the external event from the state spy, the # state should be deactivated and activated again. assert empty_statechart.is_active('spy') assert state_spy.entries is 2 assert state_spy.exits is 1
def test_choice_state_transitions(self, state_name, expected_state_name): def is_a(**kwargs): return state_name == 'a' statechart = Statechart(name='statechart') init = InitialState(statechart) state_a = State(name='a', context=statechart) state_b = State(name='b', context=statechart) choice = ChoiceState(context=statechart) Transition(start=init, end=choice) Transition(start=choice, end=state_a, event=None, guard=is_a) Transition(start=choice, end=state_b, event=None, guard=None) # else statechart.start() assert statechart.is_active(expected_state_name)