class BindTests(unittest.TestCase): def setUp(self): sc = io.import_from_yaml(open('tests/yaml/simple.yaml')) self.interpreter = Interpreter(sc) def test_bind(self): other_sc = io.import_from_yaml(open('tests/yaml/simple.yaml')) other_interpreter = Interpreter(other_sc) self.interpreter.bind(other_interpreter) self.assertEqual(self.interpreter._bound, [other_interpreter.send]) self.interpreter.send(Event('test'), internal=True) self.assertTrue(self.interpreter._events.pop(), Event('test')) self.assertTrue(other_interpreter._events.pop(), Event('test')) def test_bind_callable(self): other_sc = io.import_from_yaml(open('tests/yaml/simple.yaml')) other_interpreter = Interpreter(other_sc) self.interpreter.bind(other_interpreter.send) self.assertEqual(self.interpreter._bound, [other_interpreter.send]) self.interpreter.send(Event('test'), internal=True) self.assertTrue(self.interpreter._events.pop(), Event('test')) self.assertTrue(other_interpreter._events.pop(), Event('test'))
def test_floor_selection(self): sc = io.import_from_yaml(open('docs/examples/elevator.yaml')) interpreter = Interpreter(sc) interpreter.send(Event('floorSelected', floor=4)).execute_once() self.assertEqual(interpreter._evaluator.context['destination'], 4) interpreter.execute_once() self.assertEqual(sorted(interpreter.configuration), ['active', 'doorsClosed', 'floorListener', 'floorSelecting', 'movingElevator'])
def test_statechart_postcondition(self): sc = io.import_from_yaml(open('tests/yaml/simple.yaml')) sc.postconditions.append('False') interpreter = Interpreter(sc) interpreter.send(Event('goto s2')).send(Event('goto final')) with self.assertRaises(PostconditionFailed) as cm: interpreter.execute() self.assertTrue(isinstance(cm.exception.obj, StateChart))
def test_run_in_background(self): sc = io.import_from_yaml(open('tests/yaml/simple.yaml')) intp = Interpreter(sc) task = run_in_background(intp, 0.001) intp.send(Event('goto s2')) intp.send(Event('goto final')) task.join() self.assertTrue(intp.final)
def test_simple_entered(self): sc = io.import_from_yaml(open('tests/yaml/simple.yaml')) interpreter = Interpreter(sc, DummyEvaluator) interpreter.send(Event('goto s2')) self.assertEqual(interpreter.execute_once().entered_states, ['s2']) interpreter.send(Event('goto final')) self.assertEqual(interpreter.execute_once().entered_states, ['s3']) self.assertEqual(interpreter.execute_once().entered_states, ['final']) self.assertEqual(interpreter.configuration, []) self.assertTrue(interpreter.final)
def test_simple_configuration(self): sc = io.import_from_yaml(open('tests/yaml/simple.yaml')) interpreter = Interpreter(sc, DummyEvaluator) interpreter.execute_once() # Should do nothing! self.assertEqual(interpreter.configuration, ['s1']) interpreter.send(Event('goto s2')) interpreter.execute_once() self.assertEqual(interpreter.configuration, ['s2']) interpreter.execute_once() self.assertEqual(interpreter.configuration, ['s3'])
def test_memory(self): sc = io.import_from_yaml(open('tests/yaml/history.yaml')) interpreter = Interpreter(sc, DummyEvaluator) interpreter.send(Event('next')).execute_once() self.assertEqual(sorted(interpreter.configuration), ['loop', 's2']) step = interpreter.send(Event('pause')).execute_once() self.assertEqual(step.exited_states, ['s2', 'loop']) self.assertEqual(sorted(interpreter.configuration), ['pause'])
def test_resume_memory(self): sc = io.import_from_yaml(open('tests/yaml/history.yaml')) interpreter = Interpreter(sc, DummyEvaluator) interpreter.send(Event('next')).send(Event('pause')).send(Event('continue')) steps = interpreter.execute() step = steps[-1] self.assertEqual(step.entered_states, ['loop', 'loop.H', 's2']) self.assertEqual(step.exited_states, ['pause', 'loop.H']) self.assertEqual(sorted(interpreter.configuration), ['loop', 's2'])
def test_doorsOpen(self): sc = io.import_from_yaml(open('docs/examples/elevator.yaml')) interpreter = Interpreter(sc) interpreter.send(Event('floorSelected', floor=4)) interpreter.execute() self.assertEqual(interpreter._evaluator.context['current'], 4) interpreter.time += 10 interpreter.execute() self.assertTrue('doorsOpen' in interpreter.configuration) self.assertEqual(interpreter._evaluator.context['current'], 0)
def test_exited_order(self): sc = io.import_from_yaml(open('tests/yaml/deep_history.yaml')) interpreter = Interpreter(sc, DummyEvaluator) interpreter.send(Event('next1')).send(Event('next2')).send(Event('pause')) step = interpreter.execute()[-1] self.assertEqual(step.exited_states, ['s12', 's22', 'process_1', 'process_2', 'concurrent_processes', 'active']) self.assertEqual(sorted(interpreter.configuration), ['pause']) step = interpreter.send(Event('continue')).execute_once() self.assertEqual(step.exited_states, ['pause', 'active.H*']) interpreter.send(Event('next1')).send(Event('next2')).execute() self.assertTrue(interpreter.final)
class RemoteElevatorTests(unittest.TestCase): def setUp(self): self.elevator = Interpreter(io.import_from_yaml(open('docs/examples/elevator.yaml'))) self.buttons = Interpreter(io.import_from_yaml(open('docs/examples/elevator_buttons.yaml'))) self.buttons.bind(self.elevator) def test_button(self): self.assertEqual(self.elevator.context['current'], 0) self.buttons.send(Event('button_2_pushed')) self.buttons.execute() event = self.elevator._events.pop() self.assertEqual(event, Event('floorSelected')) self.assertEqual(event.data['floor'], 2) self.buttons.send(Event('button_2_pushed')) self.buttons.execute() self.elevator.execute() self.assertEqual(self.elevator.context['current'], 2) def test_button_0_on_groundfloor(self): self.assertEqual(self.elevator.context['current'], 0) self.buttons.send(Event('button_0_pushed')) self.buttons.execute() self.elevator.execute() self.assertEqual(self.elevator.context['current'], 0)
class WriterExecutionTests(unittest.TestCase): def setUp(self): self.sc = io.import_from_yaml(open('docs/examples/writer_options.yaml')) self.interpreter = Interpreter(self.sc) def test_output(self): scenario = [ Event('keyPress', key='bonjour '), Event('toggle'), Event('keyPress', key='a '), Event('toggle'), Event('toggle_bold'), Event('keyPress', key='tous !'), Event('leave') ] for event in scenario: self.interpreter.send(event) self.interpreter.execute() self.assertTrue(self.interpreter.final) self.assertEqual(self.interpreter.context['output'], ['bonjour ', '[b]', '[i]', 'a ', '[/b]', '[/i]', '[b]', 'tous !', '[/b]'])
class InternalTests(unittest.TestCase): def setUp(self): self.sc = io.import_from_yaml(open('tests/yaml/internal.yaml')) self.interpreter = Interpreter(self.sc) def testInternalSent(self): step = self.interpreter.execute_once() self.assertEqual(step.event.name, 'next') def testInternalBeforeExternal(self): self.interpreter.send(Event('not_next')) step = self.interpreter.execute_once() self.assertEqual(step.event.name, 'next') step = self.interpreter.execute_once() self.assertEqual(step.event, None) self.assertEqual(step.entered_states, ['s2']) step = self.interpreter.execute_once() self.assertEqual(step.event.name, 'not_next') def testActiveGuard(self): self.interpreter.execute() self.assertTrue(self.interpreter.final)
def test_after_memory(self): sc = io.import_from_yaml(open('tests/yaml/history.yaml')) interpreter = Interpreter(sc, DummyEvaluator) interpreter.send(Event('next')).send(Event('pause')).send(Event('continue')) interpreter.send(Event('next')).send(Event('next')) interpreter.execute() self.assertEqual(sorted(interpreter.configuration), ['loop', 's1']) interpreter.send(Event('pause')).send(Event('stop')) interpreter.execute() self.assertTrue(interpreter.final)
def test_deep_memory(self): sc = io.import_from_yaml(open('tests/yaml/deep_history.yaml')) interpreter = Interpreter(sc, DummyEvaluator) interpreter.send(Event('next1')).send(Event('next2')) interpreter.execute() self.assertEqual(sorted(interpreter.configuration), ['active', 'concurrent_processes', 'process_1', 'process_2', 's12', 's22']) interpreter.send(Event('error1')) interpreter.execute() self.assertEqual(interpreter.configuration, ['pause']) self.assertEqual(sorted(interpreter._memory['active.H*']), ['concurrent_processes', 'process_1', 'process_2', 's12', 's22']) interpreter.send(Event('continue')) interpreter.execute() self.assertEqual(sorted(interpreter.configuration), ['active', 'concurrent_processes', 'process_1', 'process_2', 's12', 's22'])
class StopwatchApplication(tk.Frame): def __init__(self, master=None): super().__init__(master) # Initialize widgets self.create_widgets() # Create a Stopwatch interpreter with open("stopwatch_external.yaml") as f: statechart = import_from_yaml(f) # Create a stopwatch object and pass it to the interpreter self.stopwatch = Stopwatch() self.interpreter = Interpreter( statechart, initial_context={"stopwatch": self.stopwatch}, initial_time=time.time() ) # Run the interpreter self.run() # Update the stopwatch every 100ms self.after(100, self.update_stopwatch) def update_stopwatch(self): self.stopwatch.update(delta=0.1) self.after(100, self.update_stopwatch) # Update timer label self.w_timer["text"] = self.stopwatch.display() def run(self): # Queue a call every 100ms on tk's mainloop self.interpreter.execute() self.after(100, self.run) self.w_states["text"] = "active states: " + ", ".join(self.interpreter.configuration) def create_widgets(self): self.pack() # Add buttons self.w_btn_start = tk.Button(self, text="start", command=self._start) self.w_btn_stop = tk.Button(self, text="stop", command=self._stop) self.w_btn_split = tk.Button(self, text="split", command=self._split) self.w_btn_unsplit = tk.Button(self, text="unsplit", command=self._unsplit) self.w_btn_reset = tk.Button(self, text="reset", command=self._reset) self.w_btn_quit = tk.Button(self, text="quit", command=self._quit) # Initial button states self.w_btn_stop["state"] = tk.DISABLED self.w_btn_unsplit["state"] = tk.DISABLED # Pack self.w_btn_start.pack(side=tk.LEFT) self.w_btn_stop.pack(side=tk.LEFT) self.w_btn_split.pack(side=tk.LEFT) self.w_btn_unsplit.pack(side=tk.LEFT) self.w_btn_reset.pack(side=tk.LEFT) self.w_btn_quit.pack(side=tk.LEFT) # Active states label self.w_states = tk.Label(root) self.w_states.pack(side=tk.BOTTOM, fill=tk.X) # Timer label self.w_timer = tk.Label(root, font=("Helvetica", 16), pady=5) self.w_timer.pack(side=tk.BOTTOM, fill=tk.X) def _start(self): self.interpreter.send(Event("start")) self.w_btn_start["state"] = tk.DISABLED self.w_btn_stop["state"] = tk.NORMAL def _stop(self): self.interpreter.send(Event("stop")) self.w_btn_start["state"] = tk.NORMAL self.w_btn_stop["state"] = tk.DISABLED def _reset(self): self.interpreter.send(Event("reset")) def _split(self): self.interpreter.send(Event("split")) self.w_btn_split["state"] = tk.DISABLED self.w_btn_unsplit["state"] = tk.NORMAL def _unsplit(self): self.interpreter.send(Event("split")) self.w_btn_split["state"] = tk.NORMAL self.w_btn_unsplit["state"] = tk.DISABLED def _quit(self): self.master.destroy()
class StopwatchApplication(tk.Frame): def __init__(self, master=None): super().__init__(master) # Initialize widgets self.create_widgets() # Create a Stopwatch interpreter with open('stopwatch.yaml') as f: statechart = import_from_yaml(f) self.interpreter = Interpreter(statechart, initial_time=time.time()) # Bind interpreter events to the GUI self.interpreter.bind(self.event_handler) # Run the interpreter self.run() def run(self): # This function does essentially the same job than ``sismic.interpreter.run_in_background`` # but uses Tkinter's mainloop instead of a Thread, which is more adequate. # Update internal clock and execute interpreter self.interpreter.time = time.time() self.interpreter.execute() # Queue a call every 100ms on tk's mainloop self.after(1000, self.run) # Update the widget that contains the list of active states. self.w_states['text'] = 'active states: ' + ', '.join(self.interpreter.configuration) def create_widgets(self): self.pack() # Add buttons self.w_btn_start = tk.Button(self, text='start', command=self._start) self.w_btn_stop = tk.Button(self, text='stop', command=self._stop) self.w_btn_split = tk.Button(self, text='split', command=self._split) self.w_btn_unsplit = tk.Button(self, text='unsplit', command=self._unsplit) self.w_btn_reset = tk.Button(self, text='reset', command=self._reset) self.w_btn_quit = tk.Button(self, text='quit', command=self._quit) # Initial button states self.w_btn_stop['state'] = tk.DISABLED self.w_btn_unsplit['state'] = tk.DISABLED # Pack self.w_btn_start.pack(side=tk.LEFT,) self.w_btn_stop.pack(side=tk.LEFT,) self.w_btn_split.pack(side=tk.LEFT,) self.w_btn_unsplit.pack(side=tk.LEFT,) self.w_btn_reset.pack(side=tk.LEFT,) self.w_btn_quit.pack(side=tk.LEFT,) # Active states label self.w_states = tk.Label(root) self.w_states.pack(side=tk.BOTTOM, fill=tk.X) # Timer label self.w_timer = tk.Label(root, font=("Helvetica", 16), pady=5) self.w_timer.pack(side=tk.BOTTOM, fill=tk.X) def event_handler(self, event): # Update text widget when timer value is updated if event.name == 'refresh': self.w_timer['text'] = event.time def _start(self): self.interpreter.send(Event('start')) self.w_btn_start['state'] = tk.DISABLED self.w_btn_stop['state'] = tk.NORMAL def _stop(self): self.interpreter.send(Event('stop')) self.w_btn_start['state'] = tk.NORMAL self.w_btn_stop['state'] = tk.DISABLED def _reset(self): self.interpreter.send(Event('reset')) def _split(self): self.interpreter.send(Event('split')) self.w_btn_split['state'] = tk.DISABLED self.w_btn_unsplit['state'] = tk.NORMAL def _unsplit(self): self.interpreter.send(Event('split')) self.w_btn_split['state'] = tk.NORMAL self.w_btn_unsplit['state'] = tk.DISABLED def _quit(self): self.master.destroy()
def test_do_not_raise(self): self.sc.invariants.append('False') interpreter = Interpreter(self.sc, ignore_contract=True) interpreter.send(Event('floorSelected', floor=4)) interpreter.execute()
class ElevatorContractTests(unittest.TestCase): def setUp(self): self.sc = io.import_from_yaml(open('docs/examples/elevator.yaml')) self.interpreter = Interpreter(self.sc) def test_no_error(self): self.interpreter.send(Event('floorSelected', floor=4)) self.interpreter.execute() self.assertFalse(self.interpreter.final) def test_state_precondition(self): self.sc.states['movingUp'].preconditions.append('False') self.interpreter.send(Event('floorSelected', floor=4)) with self.assertRaises(PreconditionFailed) as cm: self.interpreter.execute() self.assertTrue(isinstance(cm.exception.obj, StateMixin)) def test_state_postcondition(self): self.sc.states['movingUp'].postconditions.append('False') self.interpreter.send(Event('floorSelected', floor=4)) with self.assertRaises(PostconditionFailed) as cm: self.interpreter.execute() self.assertTrue(isinstance(cm.exception.obj, StateMixin)) def test_state_invariant(self): self.sc.states['movingUp'].invariants.append('False') self.interpreter.send(Event('floorSelected', floor=4)) with self.assertRaises(InvariantFailed) as cm: self.interpreter.execute() self.assertTrue(isinstance(cm.exception.obj, StateMixin)) def test_transition_precondition(self): self.sc.states['floorSelecting'].transitions[0].preconditions.append('False') self.interpreter.send(Event('floorSelected', floor=4)) with self.assertRaises(PreconditionFailed) as cm: self.interpreter.execute() self.assertTrue(isinstance(cm.exception.obj, Transition)) def test_transition_postcondition(self): self.sc.states['floorSelecting'].transitions[0].postconditions.append('False') self.interpreter.send(Event('floorSelected', floor=4)) with self.assertRaises(PostconditionFailed) as cm: self.interpreter.execute() self.assertTrue(isinstance(cm.exception.obj, Transition)) def test_statechart_precondition(self): self.sc.preconditions.append('False') with self.assertRaises(PreconditionFailed) as cm: self.interpreter = Interpreter(self.sc) self.assertTrue(isinstance(cm.exception.obj, StateChart)) def test_statechart_postcondition(self): sc = io.import_from_yaml(open('tests/yaml/simple.yaml')) sc.postconditions.append('False') interpreter = Interpreter(sc) interpreter.send(Event('goto s2')).send(Event('goto final')) with self.assertRaises(PostconditionFailed) as cm: interpreter.execute() self.assertTrue(isinstance(cm.exception.obj, StateChart)) def test_statechart_invariant(self): self.sc.invariants.append('False') self.interpreter.send(Event('floorSelected', floor=4)) with self.assertRaises(InvariantFailed) as cm: self.interpreter.execute() self.assertTrue(isinstance(cm.exception.obj, StateChart)) def test_do_not_raise(self): self.sc.invariants.append('False') interpreter = Interpreter(self.sc, ignore_contract=True) interpreter.send(Event('floorSelected', floor=4)) interpreter.execute()
class ParallelExecutionTests(unittest.TestCase): def setUp(self): self.sc = io.import_from_yaml(open('tests/yaml/parallel.yaml')) self.interpreter = Interpreter(self.sc) def test_concurrent_transitions(self): step = self.interpreter.send(Event('nextA')).execute_once() self.assertEqual(self.interpreter.configuration, ['s1', 'p1', 'p2', 'a1', 'a2']) self.assertLess(step.exited_states.index('initial1'), step.exited_states.index('initial2')) self.assertLess(step.entered_states.index('a1'), step.entered_states.index('a2')) def test_concurrent_transitions_nested_target(self): self.interpreter.send(Event('nextA')).send(Event('reset1')) self.interpreter.execute() self.assertEqual(self.interpreter.configuration, ['s1', 'p1', 'p2', 'a2', 'initial1']) def test_unnested_transitions(self): self.interpreter.send(Event('nextA')).send(Event('nextA')) self.interpreter.execute() self.assertEqual(self.interpreter.configuration, ['s1', 'p1', 'p2', 'a2', 'initial1']) def test_unnested_transitions_2(self): self.interpreter.send(Event('nextA')).send(Event('nextB')) self.interpreter.execute() self.assertEqual(self.interpreter.configuration, ['s1', 'p1', 'p2', 'b1', 'b2']) def test_conflicting_transitions(self): self.interpreter.send(Event('nextA')).send(Event('nextB')).send(Event('conflict1')) self.interpreter.execute_once() self.interpreter.execute_once() with self.assertRaises(Warning): self.interpreter.execute_once() def test_conflicting_transitions_2(self): self.interpreter.send(Event('nextA')).send(Event('nextB')).send(Event('conflict2')) self.interpreter.execute_once() self.interpreter.execute_once() with self.assertRaises(Warning): self.interpreter.execute_once()
def test_simple_final(self): sc = io.import_from_yaml(open('tests/yaml/simple.yaml')) interpreter = Interpreter(sc, DummyEvaluator) interpreter.send(Event('goto s2')).send(Event('goto final')) interpreter.execute() self.assertTrue(interpreter.final)
class NestedParallelExecutionTests(unittest.TestCase): def setUp(self): self.sc = io.import_from_yaml(open('tests/yaml/nested_parallel.yaml')) self.interpreter = Interpreter(self.sc) self.common_states = ['s1', 'p1', 'p2', 'r1', 'r2', 'r3', 'r4'] def test_initial(self): self.assertEqual(self.interpreter.configuration, self.common_states + ['i1', 'i2', 'i3', 'i4']) def test_parallel_order(self): self.interpreter.send(Event('next')) step = self.interpreter.execute_once() self.assertEqual(self.interpreter.configuration, self.common_states + ['j1', 'j2', 'j3', 'j4']) self.assertEqual(step.exited_states, ['i1', 'i2', 'i3', 'i4']) self.assertEqual(step.entered_states, ['j1', 'j2', 'j3', 'j4']) self.assertEqual([t.from_state for t in step.transitions], ['i1', 'i2', 'i3', 'i4']) def test_partial_parallel_order(self): self.interpreter.send(Event('next')).send(Event('click')) self.interpreter.execute_once() step = self.interpreter.execute_once() self.assertEqual(self.interpreter.configuration, self.common_states + ['j1', 'j3', 'k2', 'k4']) self.assertEqual(step.exited_states, ['j2', 'j4']) self.assertEqual(step.entered_states, ['k2', 'k4']) self.assertEqual([t.from_state for t in step.transitions], ['j2', 'j4']) def test_partial_unnested_transition(self): self.interpreter.send(Event('next')).send(Event('reset')) self.interpreter.execute_once() step = self.interpreter.execute_once() self.assertEqual(self.interpreter.configuration, self.common_states + ['i1', 'i2', 'i3', 'i4']) self.assertLess(step.exited_states.index('r2'), step.exited_states.index('r4')) self.assertLess(step.exited_states.index('p1'), step.exited_states.index('p2')) self.assertLess(step.exited_states.index('r2'), step.exited_states.index('p1')) self.assertLess(step.exited_states.index('r4'), step.exited_states.index('p2')) self.assertLess(step.entered_states.index('p1'), step.entered_states.index('p2')) self.assertLess(step.entered_states.index('p1'), step.entered_states.index('r1')) self.assertLess(step.entered_states.index('p1'), step.entered_states.index('r2')) self.assertLess(step.entered_states.index('r1'), step.entered_states.index('p2')) self.assertLess(step.entered_states.index('r2'), step.entered_states.index('p2')) self.assertLess(step.entered_states.index('p2'), step.entered_states.index('r3')) self.assertLess(step.entered_states.index('p2'), step.entered_states.index('r4')) self.assertEqual([t.from_state for t in step.transitions], ['r2', 'r4']) def test_name_order(self): self.interpreter.send(Event('next')).send(Event('click')).send(Event('next')).send(Event('next')) self.interpreter.execute_once() self.interpreter.execute_once() self.interpreter.execute_once() self.assertEqual(self.interpreter.configuration, self.common_states + ['k1', 'k3', 'x', 'y']) step = self.interpreter.execute_once() self.assertLess(step.exited_states.index('k1'), step.exited_states.index('k3')) self.assertLess(step.exited_states.index('k3'), step.exited_states.index('x')) self.assertLess(step.exited_states.index('x'), step.exited_states.index('y')) self.assertEqual(self.interpreter.configuration, self.common_states + ['k1', 'x', 'y', 'z']) self.assertLess(step.entered_states.index('k1'), step.entered_states.index('z')) self.assertLess(step.entered_states.index('z'), step.entered_states.index('x')) self.assertLess(step.entered_states.index('x'), step.entered_states.index('y')) self.assertEqual([t.from_state for t in step.transitions], ['k1', 'k3', 'x', 'y']) step = self.interpreter.send(Event('next')).execute_once() self.assertLess(step.exited_states.index('k1'), step.exited_states.index('x')) self.assertLess(step.exited_states.index('x'), step.exited_states.index('y')) self.assertLess(step.exited_states.index('y'), step.exited_states.index('z')) self.assertEqual(self.interpreter.configuration, self.common_states + ['k1', 'x', 'y', 'z']) self.assertLess(step.entered_states.index('k1'), step.entered_states.index('x')) self.assertLess(step.entered_states.index('x'), step.entered_states.index('y')) self.assertLess(step.entered_states.index('y'), step.entered_states.index('z')) self.assertEqual([t.from_state for t in step.transitions], ['k1', 'x', 'y', 'z'])