示例#1
0
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'))
示例#2
0
    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'])
示例#3
0
 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))
示例#4
0
 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)
示例#5
0
 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)
示例#6
0
 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'])
示例#7
0
    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'])
示例#8
0
    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'])
示例#9
0
    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)
示例#10
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)
示例#11
0
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)
示例#12
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]'])
示例#13
0
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)
示例#14
0
    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)
示例#15
0
    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'])
示例#16
0
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()
示例#17
0
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()
示例#18
0
 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()
示例#19
0
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()
示例#20
0
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()
示例#21
0
 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)
示例#22
0
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'])