Beispiel #1
0
    def test_blueprint_simple(self):
        states = ['A', 'B', 'C', 'D']
        # Define with list of dictionaries
        transitions = [{
            'trigger': 'walk',
            'source': 'A',
            'dest': 'B'
        }, {
            'trigger': 'run',
            'source': 'B',
            'dest': 'C'
        }, {
            'trigger': 'sprint',
            'source': 'C',
            'dest': 'D'
        }]
        m = Machine(states=states,
                    transitions=transitions,
                    before_state_change='before_state_change',
                    after_state_change='after_state_change',
                    initial='A')

        self.assertEqual(len(m.blueprints['states']), 4)
        self.assertEqual(m.blueprints['states'][3], 'D')
        self.assertEqual(len(m.blueprints['transitions']), 3)
        self.assertEqual(m.blueprints['transitions'][2]['trigger'], 'sprint')

        m.add_transition('fly', 'D', 'A')
        self.assertEqual(len(m.blueprints['transitions']), 4)
        self.assertEqual(m.blueprints['transitions'][3]['source'], 'D')
Beispiel #2
0
    def test_nested_agraph_diagram(self):
        ''' Same as above, but with nested states. '''
        states = ['A', 'B', {'name': 'C', 'children': ['1', '2', '3']}, 'D']
        transitions = [
            {'trigger': 'walk', 'source': 'A', 'dest': 'B'},   # 1 edge
            {'trigger': 'run', 'source': 'B', 'dest': 'C'},    # + 1 edge
            {'trigger': 'sprint', 'source': 'C', 'dest': 'D',  # + 3 edges
             'conditions': 'is_fast'},
            {'trigger': 'sprint', 'source': 'C', 'dest': 'B'}  # + 3 edges = 8 edges
        ]

        m = HierarchicalMachine(states=states, transitions=transitions, initial='A', auto_transitions=False)
        graph = m.get_graph()
        self.assertIsNotNone(graph)
        self.assertTrue("digraph" in str(graph))

        # Test that graph properties match the Machine
        # print((set(m.states.keys()), )
        node_names = set([n.name for n in graph.nodes()])
        self.assertEqual(set(m.states.keys()) - set('C'), node_names)

        triggers = set([n.attr['label'] for n in graph.edges()])
        for t in triggers:
            self.assertIsNotNone(getattr(m, t))

        self.assertEqual(len(graph.edges()), 8) # see above

        # write diagram to temp file
        target = tempfile.NamedTemporaryFile()
        graph.draw(target.name, prog='dot')
        self.assertTrue(os.path.getsize(target.name) > 0)

        # cleanup temp file
        target.close()
Beispiel #3
0
    def test_pickle(self):
        import sys
        if sys.version_info < (3, 4):
            import dill as pickle
        else:
            import pickle

        states = ['A', 'B', 'C', 'D']
        # Define with list of dictionaries
        transitions = [{
            'trigger': 'walk',
            'source': 'A',
            'dest': 'B'
        }, {
            'trigger': 'run',
            'source': 'B',
            'dest': 'C'
        }, {
            'trigger': 'sprint',
            'source': 'C',
            'dest': 'D'
        }]
        m = Machine(states=states, transitions=transitions, initial='A')
        m.walk()
        dump = pickle.dumps(m)
        self.assertIsNotNone(dump)
        m2 = pickle.loads(dump)
        self.assertEqual(m.state, m2.state)
        m2.run()
Beispiel #4
0
    def test_blueprint_nested(self):
        c1 = NestedState('C_1', parent='C')
        c2 = NestedState('C_2', parent='C')
        c3 = NestedState('C_3', parent='C')
        c = NestedState('C', children=[c1, c2, c3])

        states = ['A', {'name': 'B', 'on_enter': 'chirp', 'children': ['1', '2', '3']},
                  c, 'D']
        # Define with list of dictionaries
        transitions = [
            {'trigger': 'walk', 'source': 'A', 'dest': 'B','before': 'before_state_change',
             'after': 'after_state_change' },
            {'trigger': 'run', 'source': 'B', 'dest': 'C'},
            {'trigger': 'sprint', 'source': 'C', 'dest': 'D'}
        ]
        m = Machine(states=states, transitions=transitions, before_state_change='before_state_change',
                    after_state_change='after_state_change', initial='A')

        m.before_state_change = MagicMock()
        m.after_state_change = MagicMock()

        self.assertEqual(len(m.blueprints['states']), 4)
        self.assertEqual(m.blueprints['states'][3], 'D')
        self.assertEqual(len(m.blueprints['transitions']), 3)
        # transition 'walk' before should contain two calls of the same method
        self.assertEqual(len(m.blueprints['transitions'][0]['before']), 2)
        self.assertEqual(len(m.blueprints['transitions'][0]['after']), 2)
        self.assertEqual(len(m.blueprints['transitions'][1]['before']), 1)
        self.assertEqual(m.blueprints['transitions'][2]['trigger'], 'sprint')

        m.add_transition('fly', 'D', 'A')
        self.assertEqual(len(m.blueprints['transitions']), 4)
        self.assertEqual(m.blueprints['transitions'][3]['source'], 'D')
Beispiel #5
0
    def test_nested_agraph_diagram(self):
        """ Same as above, but with nested states. """
        states = ["A", "B", {"name": "C", "children": ["1", "2", "3"]}, "D"]
        transitions = [
            {"trigger": "walk", "source": "A", "dest": "B"},  # 1 edge
            {"trigger": "run", "source": "B", "dest": "C"},  # + 1 edge
            {"trigger": "sprint", "source": "C", "dest": "D", "conditions": "is_fast"},  # + 3 edges
            {"trigger": "sprint", "source": "C", "dest": "B"},  # + 3 edges = 8 edges
        ]

        m = HierarchicalMachine(states=states, transitions=transitions, initial="A", auto_transitions=False)
        graph = m.get_graph()
        self.assertIsNotNone(graph)
        self.assertTrue("digraph" in str(graph))

        # Test that graph properties match the Machine
        # print((set(m.states.keys()), )
        node_names = set([n.name for n in graph.nodes()])
        self.assertEqual(set(m.states.keys()) - set("C"), node_names)

        triggers = set([n.attr["label"] for n in graph.edges()])
        for t in triggers:
            self.assertIsNotNone(getattr(m, t))

        self.assertEqual(len(graph.edges()), 8)  # see above

        # write diagram to temp file
        target = tempfile.NamedTemporaryFile()
        graph.draw(target.name, prog="dot")
        self.assertTrue(os.path.getsize(target.name) > 0)

        # cleanup temp file
        target.close()
Beispiel #6
0
    def test_nested_agraph_diagram(self):
        ''' Same as above, but with nested states. '''
        states = ['A', 'B', {'name': 'C', 'children': ['1', '2', '3']}, 'D']
        transitions = [
            {
                'trigger': 'walk',
                'source': 'A',
                'dest': 'B'
            },  # 1 edge
            {
                'trigger': 'run',
                'source': 'B',
                'dest': 'C'
            },  # + 1 edge
            {
                'trigger': 'sprint',
                'source': 'C',
                'dest': 'D',  # + 3 edges
                'conditions': 'is_fast'
            },
            {
                'trigger': 'sprint',
                'source': 'C',
                'dest': 'B'
            }  # + 3 edges = 8 edges
        ]

        m = HierarchicalMachine(states=states,
                                transitions=transitions,
                                initial='A',
                                auto_transitions=False)
        graph = m.get_graph()
        self.assertIsNotNone(graph)
        self.assertTrue("digraph" in str(graph))

        # Test that graph properties match the Machine
        # print((set(m.states.keys()), )
        node_names = set([n.name for n in graph.nodes()])
        self.assertEqual(set(m.states.keys()) - set('C'), node_names)

        triggers = set([n.attr['label'] for n in graph.edges()])
        for t in triggers:
            self.assertIsNotNone(getattr(m, t))

        self.assertEqual(len(graph.edges()), 8)  # see above

        # write diagram to temp file
        target = tempfile.NamedTemporaryFile()
        graph.draw(target.name, prog='dot')
        self.assertTrue(os.path.getsize(target.name) > 0)

        # cleanup temp file
        target.close()
Beispiel #7
0
    def test_blueprint_nested(self):
        c1 = NestedState('C_1', parent='C')
        c2 = NestedState('C_2', parent='C')
        c3 = NestedState('C_3', parent='C')
        c = NestedState('C', children=[c1, c2, c3])

        states = [
            'A', {
                'name': 'B',
                'on_enter': 'chirp',
                'children': ['1', '2', '3']
            }, c, 'D'
        ]
        # Define with list of dictionaries
        transitions = [{
            'trigger': 'walk',
            'source': 'A',
            'dest': 'B',
            'before': 'before_state_change',
            'after': 'after_state_change'
        }, {
            'trigger': 'run',
            'source': 'B',
            'dest': 'C'
        }, {
            'trigger': 'sprint',
            'source': 'C',
            'dest': 'D'
        }]
        m = Machine(states=states,
                    transitions=transitions,
                    before_state_change='before_state_change',
                    after_state_change='after_state_change',
                    initial='A')

        m.before_state_change = MagicMock()
        m.after_state_change = MagicMock()

        self.assertEqual(len(m.blueprints['states']), 4)
        self.assertEqual(m.blueprints['states'][3], 'D')
        self.assertEqual(len(m.blueprints['transitions']), 3)
        # transition 'walk' before should contain two calls of the same method
        self.assertEqual(len(m.blueprints['transitions'][0]['before']), 2)
        self.assertEqual(len(m.blueprints['transitions'][0]['after']), 2)
        self.assertEqual(len(m.blueprints['transitions'][1]['before']), 1)
        self.assertEqual(m.blueprints['transitions'][2]['trigger'], 'sprint')

        m.add_transition('fly', 'D', 'A')
        self.assertEqual(len(m.blueprints['transitions']), 4)
        self.assertEqual(m.blueprints['transitions'][3]['source'], 'D')
Beispiel #8
0
 def test_use_machine_as_model(self):
     states = ['A', 'B', 'C', 'D']
     m = Machine(states=states, initial='A')
     m.add_transition('move', 'A', 'B')
     m.add_transition('move_to_C', 'B', 'C')
     m.move()
     self.assertEquals(m.state, 'B')
Beispiel #9
0
    def test_callbacks_duplicate(self):

        transitions = [
            {'trigger': 'walk', 'source': 'A', 'dest': 'C', 'before': 'before_state_change',
             'after': 'after_state_change'},
            {'trigger': 'run', 'source': 'B', 'dest': 'C'}
        ]

        m = Machine(None, states=['A', 'B', 'C'], transitions=transitions,
                    before_state_change='before_state_change',
                    after_state_change='after_state_change', send_event=True,
                    initial='A', auto_transitions=True)

        m.before_state_change = MagicMock()
        m.after_state_change = MagicMock()

        m.walk()
        self.assertEqual(m.before_state_change.call_count, 2)
        self.assertEqual(m.after_state_change.call_count, 2)
Beispiel #10
0
    def test_blueprint_nested(self):
        states = ['A', {'name': 'B', 'on_enter': 'chirp', 'children': ['1', '2', '3']}, 'C', 'D']
        # Define with list of dictionaries
        transitions = [
            {'trigger': 'walk', 'source': 'A', 'dest': 'B'},
            {'trigger': 'run', 'source': 'B', 'dest': 'C'},
            {'trigger': 'sprint', 'source': 'C', 'dest': 'D'}
        ]
        m = Machine(states=states, transitions=transitions, before_state_change='before_state_change',
                    after_state_change='after_state_change', initial='A')

        self.assertEqual(len(m.blueprints['states']), 4)
        self.assertEqual(m.blueprints['states'][3], 'D')
        self.assertEqual(len(m.blueprints['transitions']), 3)
        self.assertEqual(m.blueprints['transitions'][2]['trigger'], 'sprint')

        m.add_transition('fly', 'D', 'A')
        self.assertEqual(len(m.blueprints['transitions']), 4)
        self.assertEqual(m.blueprints['transitions'][3]['source'], 'D')
Beispiel #11
0
    def test_pickle(self):
        import sys
        if sys.version_info < (3, 4):
            import dill as pickle
        else:
            import pickle

        states = ['A', 'B', 'C', 'D']
        # Define with list of dictionaries
        transitions = [
            {'trigger': 'walk', 'source': 'A', 'dest': 'B'},
            {'trigger': 'run', 'source': 'B', 'dest': 'C'},
            {'trigger': 'sprint', 'source': 'C', 'dest': 'D'}
        ]
        m = Machine(states=states, transitions=transitions, initial='A')
        m.walk()
        dump = pickle.dumps(m)
        self.assertIsNotNone(dump)
        m2 = pickle.loads(dump)
        self.assertEqual(m.state, m2.state)
        m2.run()
Beispiel #12
0
    def test_blueprint_remap(self):
        states = ['1', '2', '3', 'finished']
        transitions = [{
            'trigger': 'increase',
            'source': '1',
            'dest': '2'
        }, {
            'trigger': 'increase',
            'source': '2',
            'dest': '3'
        }, {
            'trigger': 'decrease',
            'source': '3',
            'dest': '2'
        }, {
            'trigger': 'decrease',
            'source': '1',
            'dest': '1'
        }, {
            'trigger': 'reset',
            'source': '*',
            'dest': '1'
        }, {
            'trigger': 'done',
            'source': '3',
            'dest': 'finished'
        }]

        counter = Machine(states=states, transitions=transitions, initial='1')

        new_states = [
            'A', 'B', {
                'name': 'C',
                'children': counter,
                'remap': {
                    'finished': 'A'
                }
            }
        ]
        new_transitions = [
            {
                'trigger': 'forward',
                'source': 'A',
                'dest': 'B'
            },
            {
                'trigger': 'forward',
                'source': 'B',
                'dest': 'C'
            },
            {
                'trigger': 'backward',
                'source': 'C',
                'dest': 'B'
            },
            {
                'trigger': 'backward',
                'source': 'B',
                'dest': 'A'
            },
            {
                'trigger': 'calc',
                'source': '*',
                'dest': 'C'
            },
        ]

        walker = Machine(states=new_states,
                         transitions=new_transitions,
                         before_state_change='watch',
                         after_state_change='look_back',
                         initial='A')

        walker.watch = lambda: 'walk'
        walker.look_back = lambda: 'look_back'

        counter.increase()
        counter.increase()
        counter.done()
        self.assertEqual(counter.state, 'finished')

        with self.assertRaises(MachineError):
            walker.increase()
        self.assertEqual(walker.state, 'A')
        walker.forward()
        walker.forward()
        self.assertEqual(walker.state, 'C_1')
        walker.increase()
        self.assertEqual(walker.state, 'C_2')
        walker.reset()
        self.assertEqual(walker.state, 'C_1')
        walker.to_A()
        self.assertEqual(walker.state, 'A')
        walker.calc()
        self.assertEqual(walker.state, 'C_1')
        walker.increase()
        walker.increase()
        walker.done()
        self.assertEqual(walker.state, 'A')
Beispiel #13
0
 def test_transition_definitions(self):
     states = ['A', 'B', {'name': 'C', 'children': ['1', '2', '3']}, 'D']
     # Define with list of dictionaries
     transitions = [
         {'trigger': 'walk', 'source': 'A', 'dest': 'B'},
         {'trigger': 'run', 'source': 'B', 'dest': 'C'},
         {'trigger': 'sprint', 'source': 'C', 'dest': 'D'},
         {'trigger': 'run', 'source': 'C_1', 'dest': 'C_2'}
     ]
     m = Machine(states=states, transitions=transitions, initial='A')
     m.walk()
     self.assertEquals(m.state, 'B')
     m.run()
     self.assertEquals(m.state, 'C_1')
     m.run()
     self.assertEquals(m.state, 'C_2')
     # Define with list of lists
     transitions = [
         ['walk', 'A', 'B'],
         ['run', 'B', 'C'],
         ['sprint', 'C', 'D']
     ]
     m = Machine(states=states, transitions=transitions, initial='A')
     m.to_C()
     m.sprint()
     self.assertEquals(m.state, 'D')
Beispiel #14
0
    def test_ordered_transitions(self):
        states = ['beginning', 'middle', 'end']
        m = Machine(None, states)
        m.add_ordered_transitions()
        self.assertEquals(m.state, 'initial')
        m.next_state()
        self.assertEquals(m.state, 'beginning')
        m.next_state()
        m.next_state()
        self.assertEquals(m.state, 'end')
        m.next_state()
        self.assertEquals(m.state, 'initial')

        # Include initial state in loop
        m = Machine(None, states)
        m.add_ordered_transitions(loop_includes_initial=False)
        m.to_end()
        m.next_state()
        self.assertEquals(m.state, 'beginning')

        # Test user-determined sequence and trigger name
        m = Machine(None, states, initial='beginning')
        m.add_ordered_transitions(['end', 'beginning'], trigger='advance')
        m.advance()
        self.assertEquals(m.state, 'end')
        m.advance()
        self.assertEquals(m.state, 'beginning')

        # Via init argument
        m = Machine(
            None, states, initial='beginning', ordered_transitions=True)
        m.next_state()
        self.assertEquals(m.state, 'middle')
Beispiel #15
0
    def test_blueprint_reuse(self):
        states = ['1', '2', '3']
        transitions = [
            {'trigger': 'increase', 'source': '1', 'dest': '2'},
            {'trigger': 'increase', 'source': '2', 'dest': '3'},
            {'trigger': 'decrease', 'source': '3', 'dest': '2'},
            {'trigger': 'decrease', 'source': '1', 'dest': '1'},
            {'trigger': 'reset', 'source': '*', 'dest': '1'},
        ]

        counter = Machine(states=states, transitions=transitions, before_state_change='check',
                          after_state_change='clear', initial='1')

        new_states = ['A', 'B', {'name':'C', 'children': counter}]
        new_transitions = [
            {'trigger': 'forward', 'source': 'A', 'dest': 'B'},
            {'trigger': 'forward', 'source': 'B', 'dest': 'C'},
            {'trigger': 'backward', 'source': 'C', 'dest': 'B'},
            {'trigger': 'backward', 'source': 'B', 'dest': 'A'},
            {'trigger': 'calc', 'source': '*', 'dest': 'C'},
        ]

        walker = Machine(states=new_states, transitions=new_transitions, before_state_change='watch',
                         after_state_change='look_back', initial='A')

        walker.watch = lambda: 'walk'
        walker.look_back = lambda: 'look_back'
        walker.check = lambda: 'check'
        walker.clear = lambda: 'clear'

        with self.assertRaises(MachineError):
            walker.increase()
        self.assertEqual(walker.state, 'A')
        walker.forward()
        walker.forward()
        self.assertEqual(walker.state, 'C_1')
        walker.increase()
        self.assertEqual(walker.state, 'C_2')
        walker.reset()
        self.assertEqual(walker.state, 'C_1')
        walker.to_A()
        self.assertEqual(walker.state, 'A')
        walker.calc()
        self.assertEqual(walker.state, 'C_1')