class TestFactory(TestCase): def setUp(self): self.factory = MachineFactory() def test_mixins(self): machine_cls = self.factory.get_predefined() self.assertFalse(hasattr(machine_cls, 'set_edge_state')) graph_cls = self.factory.get_predefined(graph=True) self.assertTrue(hasattr(graph_cls, 'set_edge_state')) nested_cls = self.factory.get_predefined(nested=True) self.assertFalse(hasattr(nested_cls, 'set_edge_state')) self.assertTrue(hasattr(nested_cls, 'traverse')) locked_cls = self.factory.get_predefined(locked=True) self.assertFalse(hasattr(locked_cls, 'set_edge_state')) self.assertFalse(hasattr(locked_cls, 'traverse')) self.assertTrue('__getattribute__' in locked_cls.__dict__) locked_nested_cls = self.factory.get_predefined(nested=True, locked=True) self.assertFalse(hasattr(locked_nested_cls, 'set_edge_state')) self.assertTrue(hasattr(locked_nested_cls, 'traverse')) self.assertEqual(locked_nested_cls.__getattribute__, locked_cls.__getattribute__) self.assertNotEqual(machine_cls.__getattribute__, locked_cls.__getattribute__) graph_locked_cls = self.factory.get_predefined(graph=True, locked=True) self.assertTrue(hasattr(graph_locked_cls, 'set_edge_state')) self.assertEqual(graph_locked_cls.__getattribute__, locked_cls.__getattribute__) graph_nested_cls = self.factory.get_predefined(graph=True, nested=True) self.assertNotEqual(nested_cls._create_transition, graph_nested_cls._create_transition) locked_nested_graph_cls = self.factory.get_predefined(nested=True, locked=True, graph=True) self.assertNotEqual(locked_nested_graph_cls._create_event, graph_cls._create_event)
def setUp(self): self.states = [ 'A', 'B', { 'name': 'C', 'children': ['1', '2', { 'name': '3', 'children': ['a', 'b', 'c'] }] } ] self.transitions = [{ 'trigger': 'walk', 'source': 'A', 'dest': 'C_1' }, { 'trigger': 'run', 'source': 'C_1', 'dest': 'C_3_a' }, { 'trigger': 'sprint', 'source': 'C', 'dest': 'B' }] self.machine_cls = MachineFactory.get_predefined(nested=True, graph=True) self.num_trans = len(self.transitions) self.num_auto = len(self.states) * 9
def test_ordered_with_graph(self): GraphMachine = MachineFactory.get_predefined(graph=True, nested=True) states = [ 'A', 'B', { 'name': 'C', 'children': ['1', '2', { 'name': '3', 'children': ['a', 'b', 'c'] }] }, 'D', 'E', 'F' ] State.separator = '/' machine = GraphMachine('self', states, initial='A', auto_transitions=False, ignore_invalid_triggers=True) machine.add_ordered_transitions(trigger='next_state') machine.next_state() self.assertEqual(machine.state, 'B') target = tempfile.NamedTemporaryFile() machine.get_graph().draw(target.name, prog='dot') self.assertTrue(getsize(target.name) > 0) target.close()
def setUp(self): NestedState.separator = '_' states = ['A', 'B', {'name': 'C', 'children': ['1', '2', {'name': '3', 'children': ['a', 'b', 'c']}]}, 'D', 'E', 'F'] self.stuff = Stuff(states, machine_cls=MachineFactory.get_predefined(locked=True, graph=True, nested=True)) self.stuff.heavy_processing = heavy_processing self.stuff.machine.add_transition('forward', '*', 'B', before='heavy_processing')
def setUp(self): State.separator = state_separator states = ['A', 'B', {'name': 'C', 'children': ['1', '2', {'name': '3', 'children': ['a', 'b', 'c']}]}, 'D', 'E', 'F'] machine_cls = MachineFactory.get_predefined(graph=True, nested=True) self.stuff = Stuff(states, machine_cls)
def test_agraph_diagram(self): states = ['A', 'B', 'C', 'D'] transitions = [ {'trigger': 'walk', 'source': 'A', 'dest': 'B'}, {'trigger': 'run', 'source': 'B', 'dest': 'C'}, {'trigger': 'sprint', 'source': 'C', 'dest': 'D', 'conditions': 'is_fast'}, {'trigger': 'sprint', 'source': 'C', 'dest': 'B'} ] machine_cls = MachineFactory.get_predefined(graph=True) m = machine_cls(states=states, transitions=transitions, initial='A', auto_transitions=False, title='a test') graph = m.get_graph() self.assertIsNotNone(graph) self.assertTrue("digraph" in str(graph)) # Test that graph properties match the Machine self.assertEqual( set(m.states.keys()), set([n.name for n in graph.nodes()])) triggers = set([n.attr['label'] for n in graph.edges()]) for t in triggers: t = edge_label_from_transition_label(t) self.assertIsNotNone(getattr(m, t)) self.assertEqual(len(graph.edges()), len(transitions)) # check for a valid pygraphviz diagram # 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() print(graph)
def setUp(self): class States(enum.Enum): RED = 1 YELLOW = 2 GREEN = 3 self.machine_cls = MachineFactory.get_predefined() self.States = States
class TestNestedStateEnums(TestEnumsAsStates): machine_cls = MachineFactory.get_predefined(nested=True) def test_root_enums(self): states = [ self.States.RED, self.States.YELLOW, { 'name': self.States.GREEN, 'children': ['tick', 'tock'], 'initial': 'tick' } ] m = self.machine_cls(states=states, initial=self.States.GREEN) self.assertTrue(m.is_GREEN(allow_substates=True)) self.assertTrue(m.is_GREEN_tick()) m.to_RED() self.assertTrue(m.state is self.States.RED) # self.assertEqual(m.state, self.States.GREEN) def test_nested_enums(self): # Nested enums are currently not support since model.state does not contain any information about parents # and nesting states = [ 'A', 'B', { 'name': 'C', 'children': self.States, 'initial': self.States.GREEN } ] with self.assertRaises(AttributeError): # NestedState will raise an error when parent is not None and state name is an enum # Initializing this would actually work but `m.to_A()` would raise an error in get_state(m.state) # as Machine is not aware of the location of States.GREEN m = self.machine_cls(states=states, initial='C')
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', # + 1 edges 'conditions': 'is_fast' }, { 'trigger': 'sprint', 'source': 'C', 'dest': 'B' } # + 1 edges = 4 edges ] hsm_graph_cls = MachineFactory.get_predefined(graph=True, nested=True) m = hsm_graph_cls(states=states, transitions=transitions, initial='A', auto_transitions=False, title='A test', show_conditions=True) 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: t = edge_label_from_transition_label(t) self.assertIsNotNone(getattr(m, t)) self.assertEqual(len(graph.edges()), 4) # see above m.walk() m.run() # 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()
def test_ordered_with_graph(self): GraphMachine = MachineFactory.get_predefined(graph=True, nested=True) class CustomHierarchicalGraphMachine(GraphMachine): state_cls = self.state_cls states = [ 'A', 'B', { 'name': 'C', 'children': ['1', '2', { 'name': '3', 'children': ['a', 'b', 'c'] }] }, 'D', 'E', 'F' ] machine = CustomHierarchicalGraphMachine('self', states, initial='A', auto_transitions=False, ignore_invalid_triggers=True, use_pygraphviz=False) machine.add_ordered_transitions(trigger='next_state') machine.next_state() self.assertEqual(machine.state, 'B') target = tempfile.NamedTemporaryFile(suffix='.png', delete=False) machine.get_graph().draw(target.name, prog='dot') self.assertTrue(getsize(target.name) > 0) target.close() unlink(target.name)
def setUp(self): super(TestAsync, self).setUp() self.machine_cls = MachineFactory.get_predefined(nested=True, asyncio=True) self.machine = self.machine_cls(states=['A', 'B', 'C'], transitions=[['go', 'A', 'B']], initial='A')
def test_add_custom_state(self): states = ['A', 'B', 'C', 'D'] transitions = [{ 'trigger': 'walk', 'source': 'A', 'dest': 'B' }, { 'trigger': 'run', 'source': 'B', 'dest': 'C' }, { 'trigger': 'sprint', 'source': 'C', 'dest': 'D', 'conditions': 'is_fast' }, { 'trigger': 'sprint', 'source': 'C', 'dest': 'B' }] machine_cls = MachineFactory.get_predefined(graph=True) m = machine_cls(states=states, transitions=transitions, initial='A', auto_transitions=False, title='a test') m.add_state('X') m.add_transition('foo', '*', 'X') m.foo()
def test_context_managers(self): class CounterContext(object): def __init__(self): self.counter = 0 self.level = 0 self.max = 0 super(CounterContext, self).__init__() def __enter__(self): self.counter += 1 self.level += 1 self.max = max(self.level, self.max) def __exit__(self, *exc): self.level -= 1 M = MachineFactory.get_predefined(locked=True) c = CounterContext() m = M(states=['A', 'B', 'C', 'D'], transitions=[['reset', '*', 'A']], initial='A', machine_context=c) m.get_triggers('A') self.assertEqual(c.max, 1) # was 3 before self.assertEqual(c.counter, 4) # was 72 (!) before
def test_agraph_diagram(self): states = ['A', 'B', 'C', 'D'] transitions = [ {'trigger': 'walk', 'source': 'A', 'dest': 'B'}, {'trigger': 'run', 'source': 'B', 'dest': 'C'}, {'trigger': 'sprint', 'source': 'C', 'dest': 'D', 'conditions': 'is_fast'}, {'trigger': 'sprint', 'source': 'C', 'dest': 'B'} ] machine_cls = MachineFactory.get_predefined(graph=True) m = machine_cls(states=states, transitions=transitions, initial='A', auto_transitions=False, title='a test') graph = m.get_graph() self.assertIsNotNone(graph) self.assertTrue("digraph" in str(graph)) # Test that graph properties match the Machine self.assertEqual( set(m.states.keys()), set([n.name for n in graph.nodes()])) triggers = set([n.attr['label'] for n in graph.edges()]) for t in triggers: self.assertIsNotNone(getattr(m, t)) self.assertEqual(len(graph.edges()), len(transitions)) # check for a valid pygraphviz diagram # 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() print(graph)
def setUp(self): self.machine_cls = MachineFactory.get_predefined(locked=True) self.stuff = Stuff(machine_cls=self.machine_cls) self.stuff.heavy_processing = heavy_processing self.stuff.machine.add_transition('forward', 'A', 'B', before='heavy_processing')
def setUp(self): self.machine_cls = MachineFactory.get_predefined(graph=True) self.states = ['A', 'B', 'C', 'D'] self.transitions = [ {'trigger': 'walk', 'source': 'A', 'dest': 'B'}, {'trigger': 'run', 'source': 'B', 'dest': 'C'}, {'trigger': 'sprint', 'source': 'C', 'dest': 'D', 'conditions': 'is_fast'}, {'trigger': 'sprint', 'source': 'C', 'dest': 'B'} ]
def setUp(self): machine_cls = MachineFactory.get_predefined(locked=True, nested=True, graph=True) @add_state_features(Error, Timeout, Volatile) class CustomMachine(machine_cls): pass super(TestStatesDiagramsLockedNested, self).setUp() self.machine_cls = CustomMachine
def get_wf_graph(self): """Get the graph for this machine.""" diagram_cls = MachineFactory.get_predefined(graph=True, nested=True) self.machine = diagram_cls( model=self, auto_transitions=False, title=type(self).__name__, **self.status_class.get_kwargs() # noqa: C815 ) return self.machine.get_graph()
def test_store_nested_agraph_diagram(self): ''' Same as above, but with nested states. ''' states = [ 'standing', 'walking', { 'name': 'caffeinated', 'children': ['dithering', 'running'] } ] transitions = [ ['walk', 'standing', 'walking'], # 1 edge ['go', 'standing', 'walking'], # (edge will be merged with previous edge) ['stop', 'walking', 'standing'], # + 1 edge ['drink', '*', 'caffeinated_dithering'], # + 4 edges ['walk', 'caffeinated_dithering', 'caffeinated_running'], # + 1 edge ['relax', 'caffeinated', 'standing'] # + 1 edge = 8 edges ] hsm_graph_cls = MachineFactory.get_predefined(graph=True, nested=True) m = hsm_graph_cls(states=states, transitions=transitions, initial='standing', 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()]) node_names.add('caffeinated') self.assertEqual(set(m.states.keys()), node_names) triggers = set([n.attr['label'] for n in graph.edges()]) for t in triggers: t = edge_label_from_transition_label(t) self.assertIsNotNone(getattr(m, t)) self.assertEqual(len(graph.edges()), 8) # see above # Force a new graph2 = m.get_graph(title="Second Graph", force_new=True) self.assertIsNotNone(graph) self.assertTrue("digraph" in str(graph)) self.assertFalse(graph == graph2) # 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()
def setUp(self): self.machine_cls = MachineFactory.get_predefined(graph=True, nested=True) self.states = ['A', 'B', {'name': 'C', 'children': [{'name': '1', 'children': ['a', 'b', 'c']}, '2', '3']}, 'D'] self.transitions = [ {'trigger': 'walk', 'source': 'A', 'dest': 'B'}, # 1 edge {'trigger': 'run', 'source': 'B', 'dest': 'C'}, # + 1 edge {'trigger': 'sprint', 'source': 'C', 'dest': 'D', # + 1 edge 'conditions': 'is_fast'}, {'trigger': 'sprint', 'source': 'C', 'dest': 'B'}, # + 1 edge {'trigger': 'reset', 'source': '*', 'dest': 'A'}] # + 8 edges = 12
def setUp(self): self.event_list = [] self.c1 = self.TestContext(event_list=self.event_list) self.c2 = self.TestContext(event_list=self.event_list) self.stuff = Stuff( machine_cls=MachineFactory.get_predefined(locked=True), extra_kwargs={'context': [self.c1, self.c2]}) del self.event_list[:] self.stuff.machine.add_transition('forward', 'A', 'B')
def test_multiple_models(self): class Model(object): pass s1, s2 = Model(), Model() m = MachineFactory.get_predefined(nested=True)(model=[s1, s2], states=['A', 'B', 'C'], initial='A') self.assertEquals(len(m.models), 2) m.add_transition('advance', 'A', 'B') self.assertNotEqual(s1.advance, s2.advance) s1.advance() self.assertEquals(s1.state, 'B') self.assertEquals(s2.state, 'A')
def setUp(self): self.states = [ 'A', 'B', { 'name': 'C', 'children': ['1', '2', { 'name': '3', 'children': ['a', 'b', 'c'] }] }, 'D', 'E', 'F' ] self.machine_cls = MachineFactory.get_predefined(nested=True) self.state_cls = NestedState self.stuff = Stuff(self.states, self.machine_cls)
def test_reuse_machine_config(self): simple_config = { "name": "Child", "states": ["1", "2"], "transitions": [['go', '1', '2']], "initial": "1" } simple_cls = MachineFactory.get_predefined() simple = simple_cls(**simple_config) self.assertTrue(simple.is_1()) self.assertTrue(simple.go()) self.assertTrue(simple.is_2()) machine = self.machine_cls(states=['A', simple_config], initial='A') machine.to_Child() machine.go() self.assertTrue(machine.is_Child_2())
def setUp(self): self.event_list = [] self.s1 = DummyModel() self.c1 = TestContext(event_list=self.event_list) self.c2 = TestContext(event_list=self.event_list) self.c3 = TestContext(event_list=self.event_list) self.c4 = TestContext(event_list=self.event_list) self.stuff = Stuff( machine_cls=MachineFactory.get_predefined(locked=True), extra_kwargs={'machine_context': [self.c1, self.c2]}) self.stuff.machine.add_model(self.s1, model_context=[self.c3, self.c4]) del self.event_list[:] self.stuff.machine.add_transition('forward', 'A', 'B')
def test_ordered_with_graph(self): GraphMachine = MachineFactory.get_predefined(graph=True, nested=True) states = ['A', 'B', {'name': 'C', 'children': ['1', '2', {'name': '3', 'children': ['a', 'b', 'c']}]}, 'D', 'E', 'F'] State.separator = '/' machine = GraphMachine('self', states, initial='A', auto_transitions=False, ignore_invalid_triggers=True) machine.add_ordered_transitions(trigger='next_state') machine.next_state() self.assertEqual(machine.state, 'B') target = tempfile.NamedTemporaryFile() machine.get_graph().draw(target.name, prog='dot') self.assertTrue(getsize(target.name) > 0) target.close()
def setUp(self): self.event_list = [] self.s1 = DummyModel() self.c1 = TestContext(event_list=self.event_list) self.c2 = TestContext(event_list=self.event_list) self.c3 = TestContext(event_list=self.event_list) self.c4 = TestContext(event_list=self.event_list) self.stuff = Stuff(machine_cls=MachineFactory.get_predefined(locked=True), extra_kwargs={ 'machine_context': [self.c1, self.c2] }) self.stuff.machine.add_model(self.s1, model_context=[self.c3, self.c4]) del self.event_list[:] self.stuff.machine.add_transition('forward', 'A', 'B')
def test_store_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', # + 1 edges 'conditions': 'is_fast'}, {'trigger': 'sprint', 'source': 'C', 'dest': 'B'} # + 1 edges = 4 edges ] hsm_graph_cls = MachineFactory.get_predefined(graph=True, nested=True) m = hsm_graph_cls(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: t = edge_label_from_transition_label(t) self.assertIsNotNone(getattr(m, t)) self.assertEqual(len(graph.edges()), 4) # see above # Force a new graph2 = m.get_graph(title="Second Graph", force_new=True) self.assertIsNotNone(graph) self.assertTrue("digraph" in str(graph)) self.assertFalse(graph == graph2) # 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()
def setUp(self): states = [ 'A', 'B', { 'name': 'C', 'children': ['1', '2', { 'name': '3', 'children': ['a', 'b', 'c'] }] }, 'D', 'E', 'F' ] machine_cls = MachineFactory.get_predefined(nested=True, async=True) class AsyncCompatibilityEventClass(AsyncNestedEvent): def trigger(self, model, *args, **kwargs): loop = asyncio.get_event_loop() return loop.run_until_complete(super().trigger( model, *args, **kwargs)) machine_cls.event_cls = AsyncCompatibilityEventClass self.stuff = Stuff(states, machine_cls)
def test_if_multiple_edges_are_supported(self): transitions = [ ['event_0', 'a', 'b'], ['event_1', 'a', 'b'], ['event_2', 'a', 'b'], ['event_3', 'a', 'b'], ] machine_cls = MachineFactory.get_predefined(graph=True) m = machine_cls( states=['a', 'b'], transitions=transitions, initial='a', auto_transitions=False, ) graph = m.get_graph() self.assertIsNotNone(graph) self.assertTrue("digraph" in str(graph)) triggers = [transition[0] for transition in transitions] for trigger in triggers: self.assertTrue(trigger in str(graph))
class CustomMachine(MachineFactory.get_predefined(nested=True)): state_cls = CustomState
def setUp(self): self.factory = MachineFactory()
def setUp(self): self.stuff = Stuff(machine_cls=MachineFactory.get_predefined(locked=True)) self.stuff.heavy_processing = heavy_processing self.stuff.machine.add_transition('process', '*', 'B', before='heavy_processing')
the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Janitoo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Janitoo. If not, see <http://www.gnu.org/licenses/>. """ __author__ = 'Sébastien GALLET aka bibi21000' __email__ = '*****@*****.**' __copyright__ = "Copyright © 2013-2014-2015-2016 Sébastien GALLET aka bibi21000" import logging logger = logging.getLogger(__name__) from transitions import State from transitions.extensions import MachineFactory def show_graph(self,fname='state.png', prog='dot', title=None): self.graph.draw(fname, prog=prog) Machine = MachineFactory.get_predefined(graph=True) Machine.show_graph = show_graph HierarchicalMachine = MachineFactory.get_predefined(graph=True, nested=True) HierarchicalMachine.show_graph = show_graph