예제 #1
0
    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')
예제 #2
0
    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')
예제 #3
0
    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()
예제 #4
0
    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')
예제 #5
0
    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')
예제 #6
0
    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')
예제 #7
0
    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'))
예제 #8
0
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"))
예제 #9
0
    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
예제 #10
0
    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')
예제 #11
0
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
예제 #12
0
    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)
예제 #13
0
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
예제 #14
0
    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)
예제 #15
0
    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
예제 #16
0
    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]
예제 #17
0
    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"))
예제 #18
0
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
예제 #19
0
    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())
예제 #20
0
        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)
예제 #21
0
    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')
예제 #22
0
    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
예제 #23
0
        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)
예제 #24
0
    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
예제 #25
0
    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)
예제 #26
0
    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')
예제 #27
0
    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
예제 #28
0
    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()
예제 #29
0
    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
예제 #30
0
    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
예제 #31
0
    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)