Exemple #1
0
def import_from_dict(data: Mapping[str, Any]) -> Statechart:
    data = data['statechart']

    statechart = Statechart(name=data['name'],
                            description=data.get('description', None),
                            preamble=data.get('preamble', None))

    states = []  # (StateMixin instance, parent name)
    transitions = []  # Transition instances
    # (State dict, parent name)
    data_to_consider = [
        (data['root state'], None)
    ]  # type: List[Tuple[Mapping[str, Any], Optional[str]]]

    while data_to_consider:
        state_data, parent_name = data_to_consider.pop()

        # Get state
        try:
            state = _import_state_from_dict(state_data)
        except StatechartError:
            raise
        except Exception as e:
            raise StatechartError('Unable to load given YAML') from e
        states.append((state, parent_name))

        # Get substates
        if isinstance(state, CompoundState):
            for substate_data in state_data['states']:
                data_to_consider.append((substate_data, state.name))
        elif isinstance(state, OrthogonalState):
            for substate_data in state_data['parallel states']:
                data_to_consider.append((substate_data, state.name))

        # Get transition(s)
        for transition_data in state_data.get('transitions', []):
            try:
                transition = _import_transition_from_dict(
                    state.name, transition_data)
            except StatechartError:
                raise
            except Exception as e:
                raise StatechartError('Unable to load given YAML') from e
            transitions.append(transition)

    # Register on statechart
    for state, parent in states:
        statechart.add_state(state, parent)
    for transition in transitions:
        statechart.add_transition(transition)

    return statechart
Exemple #2
0
def import_from_dict(data: dict) -> Statechart:
    data = data['statechart']

    statechart = Statechart(name=data['name'],
                            description=data.get('description', None),
                            preamble=data.get('preamble', None))

    states = []  # (StateMixin instance, parent name)
    transitions = []  # Transition instances
    # (State dict, parent name)
    data_to_consider = [(data['root state'], None)]  # type: List[Tuple[dict, Optional[str]]]

    while data_to_consider:
        state_data, parent_name = data_to_consider.pop()

        # Get state
        try:
            state = _import_state_from_dict(state_data)
        except StatechartError:
            raise
        except Exception as e:
            raise StatechartError('Unable to load given YAML') from e
        states.append((state, parent_name))

        # Get substates
        if isinstance(state, CompoundState):
            for substate_data in state_data['states']:
                data_to_consider.append((substate_data, state.name))
        elif isinstance(state, OrthogonalState):
            for substate_data in state_data['parallel states']:
                data_to_consider.append((substate_data, state.name))

        # Get transition(s)
        for transition_data in state_data.get('transitions', []):
            try:
                transition = _import_transition_from_dict(state.name, transition_data)
            except StatechartError:
                raise
            except Exception as e:
                raise StatechartError('Unable to load given YAML') from e
            transitions.append(transition)

    # Register on statechart
    for state, parent in states:
        statechart.add_state(state, parent)
    for transition in transitions:
        statechart.add_transition(transition)

    return statechart
def import_from_amola(xmi_path: str, *, ignore_validation: bool=False) -> Statechart:
    """
    Experiment support to import a statechart from AMOLA.

    :param xmi_path: path to an xmi file
    :param ignore_validation: Set to True to bypass statechart validation.
    :return: a statechart instance
    """
    _, rset = load_metamodel()

    resource = rset.get_resource(URI(xmi_path))
    model = resource.contents[0]

    # Create statechart
    if model.metadata:
        try:
            statechart = Statechart(**json.loads(model.metadata))
        except json.JSONDecodeError as e:
            warnings.warn('Invalid model metadata. Expected a JSON field, got "{}".'.format(model.metadata))
    else:
        statechart = Statechart(os.path.split(xmi_path)[-1])

    # Create states
    state_klasses = {
        'BASIC': BasicState,
        'OR': CompoundState,
        'AND': OrthogonalState,
        'HISTORY': DeepHistoryState,
        'SHALLOW_HISTORY': ShallowHistoryState,
        'END': FinalState,
    }

    # State name
    def name(node):
        return node.name if node.name else 's'+str(id(node))

    nodes = list(model.nodes)
    parents = {}
    while len(nodes) > 0:
        node = nodes.pop()

        state_klass = state_klasses.get(node.type, None)

        if state_klass:
            state = state_klass(name(node))

            try:
                metadata = json.loads(node.metadata) if node.metadata else {}
            except json.JSONDecodeError:
                warnings.warn('Invalid metadata for node {}. Expected a JSON field, got "{}".'.format(node.name, node.metadata))
                metadata = {}

            state.preconditions = metadata.get('preconditions', [])
            state.postconditions = metadata.get('postconditions', [])
            state.invariants = metadata.get('invariants', [])

            parents[name(node)] = name(node.Father) if node.Father else None
            statechart.add_state(state, parents.get(name(node), None))
            nodes.extend(list(node.Children))

            if node.actions:
                for action in node.actions.split('\n'):
                    if action.startswith('entry /'):
                        state.on_entry = action[7:].strip()
                    elif action.startswith('exit /'):
                        state.on_exit = action[6:].strip()
                    elif len(action.strip()) > 0:
                        # Static reactions should be encoded as internal transitions
                        event, guard, action = import_TE(action)
                        if event or guard or action:
                            tr = Transition(
                                source=name(node),
                                target=None,
                                event=event,
                                guard=guard,
                                action=action
                            )
                            statechart.add_transition(tr)

    # Create transitions
    for transition in model.transitions:
        try:
            metadata = json.loads(transition.metadata) if transition.metadata else {}
        except json.JSONDecodeError:
            warnings.warn('Invalid metadata for transition {} -> {}. Expected a JSON field, got "{}".'.format(
                transition.source.name, transition.target.name, transition.metadata))
            metadata = {}

        # Convert START state transition to state.initial
        if transition.source.type == 'START':
            statechart.state_for(name(transition.source.Father)).initial = name(transition.target)
        # Convert HISTORY/SHALLOW_HISTORY state transition to state.memory
        elif transition.source.type in ['HISTORY', 'SHALLOW_HISTORY']:
            statechart.state_for(name(transition.source)).memory = name(transition.target)
        else:
            # Parse Transition Expression
            event, guard, action = import_TE(transition.TE)

            tr = Transition(
                source=name(transition.source),
                target=None if metadata.get('internal', False) else name(transition.target),
                event=event,
                guard=guard,
                action=action,
            )
            tr.preconditions = metadata.get('preconditions', [])
            tr.postconditions = metadata.get('postconditions', [])
            tr.invariants = metadata.get('invariants', [])

            statechart.add_transition(tr)

    # Validate, if required
    if not ignore_validation:
        statechart.validate()

    return statechart
class PropertiesTests(unittest.TestCase):
    def setUp(self):
        self.sequential_statechart = Statechart('tested_statechart')
        initial_state = CompoundState('initial_state', initial='a_state')
        a_state = BasicState('a_state')
        b_state = BasicState('b_state')
        self.sequential_statechart.add_state(initial_state, None)
        self.sequential_statechart.add_state(a_state, 'initial_state')
        self.sequential_statechart.add_state(b_state, 'initial_state')
        self.sequential_statechart.add_transition(Transition(source='a_state', target='b_state', event='event'))

    def generic_test(self,
                     tested_statechart: Statechart,
                     events: list,
                     property: Condition,
                     expected_success: bool,
                     expected_failure: bool):
        from sismic.interpreter import log_trace
        tester_statechart = Statechart('tester_statechart')

        tester_statechart.add_state(OrthogonalState('parallel_state'), None)
        tester_statechart.add_state(CompoundState('testing_area', initial='property'), parent='parallel_state')
        tester_statechart.add_state(BasicState('success_state'), parent='testing_area')
        tester_statechart.add_state(BasicState('failure_state'), parent='testing_area')

        property.add_to(tester_statechart,
                        id='property',
                        parent_id='testing_area',
                        status_id='parallel_state',
                        success_id='success_state',
                        failure_id='failure_state')

        tester_interpreter = Interpreter(tester_statechart)

        self.assertFalse('success_state' in tester_interpreter.configuration)
        self.assertFalse('failure_state' in tester_interpreter.configuration)

        tested_interpreter = Interpreter(tested_statechart)
        trace = log_trace(tested_interpreter)

        for event in events:
            tested_interpreter.queue(event)

        tested_interpreter.execute()

        story = teststory_from_trace(trace)
        story.tell(tester_interpreter)

        self.assertEqual(expected_success, 'success_state' in tester_interpreter.configuration)
        self.assertEqual(expected_failure, 'failure_state' in tester_interpreter.configuration)

    def test_condition_success(self):
        for (event, condition) in [
            ('event', EnterState('b_state')),
            ('event', EnterState('foo', 'b_state', 'bar')),
            ('event', EnterAnyState()),
            ('event', ExitState('a_state')),
            ('event', ExitState('foo', 'a_state', 'bar')),
            ('event', ExitAnyState()),
            ('foo', ConsumeEvent('foo')),
            ('foo', ConsumeEvent('foo', 'bar')),
            ('foo', ConsumeAnyEvent()),
            ('foo', ConsumeAnyEventBut('bar')),
            ('foo', ConsumeAnyEventBut('bar', 'baz')),
            ('foo', ExecutionStart()),
            ('foo', ExecutionStop()),
            ('foo', StartStep()),
            ('foo', ExecutionStop()),
            ('event', TransitionProcess()),
            ('event', TransitionProcess(event='event')),
            ('event', TransitionProcess(source='a_state')),
            ('event', TransitionProcess(target='b_state')),
            ('event', Before(EnterState('a_state'), EnterState('b_state'))),
            ('event', Before(EnterState('a_state'), FalseCondition())),
            ('event', Before(EnterState('a_state'), UndeterminedCondition())),
            ('event', Before(EnterState('b_state'), FalseCondition())),
            ('event', Before(EnterState('b_state'), UndeterminedCondition())),

        ]:
            with self.subTest(condition=condition):
                self.generic_test(self.sequential_statechart, [Event(event)], condition, True, False)

    def test_condition_faillure(self):
        for (event, condition) in [
            ('event', Before(EnterState('b_state'), EnterState('a_state'))),
        ]:
            with self.subTest(condition=condition):
                self.generic_test(self.sequential_statechart, [Event(event)], condition, False, True)

    def test_condition_undetermined(self):
        for (event, condition) in [
            ('event', EnterState('foo')),
            ('event', ExitState('b_state')),
            ('foo', ExitAnyState()),
            ('bar', ConsumeEvent('foo')),
            ('foo', TransitionProcess()),
            ('event', TransitionProcess(event='foo')),
            ('event', TransitionProcess(source='foo')),
            ('event', TransitionProcess(target='foo')),
            ('event', TransitionProcess(event='')),

        ]:
            with self.subTest(condition=condition):
                self.generic_test(self.sequential_statechart, [Event(event)], condition, False, False)

    def test_check_guard_success(self):
        statechart = Statechart('statechart')
        statechart.add_state(OrthogonalState('parallel_state'), parent=None)

        initial_state = CompoundState('initial_state', initial='condition')
        statechart.add_state(initial_state, parent='parallel_state')

        statechart.add_state(BasicState('success'), parent='initial_state')
        statechart.add_state(BasicState('failure'), parent='initial_state')

        CheckGuard('x == 1').add_to(statechart=statechart,
                                    id='condition',
                                    parent_id='initial_state',
                                    status_id='parallel_state',
                                    success_id='success',
                                    failure_id='failure')

        interpreter = Interpreter(statechart)
        interpreter.context['x'] = 1

        interpreter.execute()
        self.assertTrue('success' in interpreter.configuration)
        self.assertFalse('failure' in interpreter.configuration)

    def test_check_guard_failure(self):
        statechart = Statechart('statechart')
        statechart.add_state(OrthogonalState('parallel_state'), parent=None)

        initial_state = CompoundState('initial_state', initial='condition')
        statechart.add_state(initial_state, parent='parallel_state')
        statechart.add_state(BasicState('success'), parent='initial_state')
        statechart.add_state(BasicState('failure'), parent='initial_state')

        CheckGuard('x == 1').add_to(statechart=statechart,
                                    id='condition',
                                    parent_id='initial_state',
                                    status_id='parallel_state',
                                    success_id='success',
                                    failure_id='failure')

        interpreter = Interpreter(statechart)
        interpreter.context['x'] = 42

        interpreter.execute()
        self.assertFalse('success' in interpreter.configuration)
        self.assertTrue('failure' in interpreter.configuration)



    def test_consume_any_event_but_wrong(self):
        self.generic_test(self.sequential_statechart, [], ConsumeAnyEventBut('foo'), False, False)

    def test_transition_process_right_eventless(self):
        self.sequential_statechart.remove_transition(Transition(source='a_state', target='b_state', event='event'))
        self.sequential_statechart.add_transition(Transition(source='a_state', target='b_state'))

        self.generic_test(self.sequential_statechart,
                          [Event('event')],
                          TransitionProcess(event=''),
                          True,
                          False)

    def test_active_state_right(self):
        self.sequential_statechart.remove_transition(Transition(source='a_state', target='b_state', event='event'))

        self.generic_test(self.sequential_statechart,
                          [Event('event')],
                          Then(ConsumeEvent('event'), ActiveState(state='a_state')),
                          True,
                          False)

    def test_active_state_wrong(self):
        self.sequential_statechart.remove_transition(Transition(source='a_state', target='b_state', event='event'))

        self.generic_test(self.sequential_statechart,
                          [Event('event')],
                          Then(ConsumeEvent('event'), ActiveState(state='b_state')),
                          False,
                          True)

    def test_inactive_state_right(self):
        self.sequential_statechart.remove_transition(Transition(source='a_state', target='b_state', event='event'))

        self.generic_test(self.sequential_statechart,
                          [Event('event')],
                          Then(ConsumeEvent('event'), InactiveState(state='b_state')),
                          True,
                          False)

    def test_inactive_state_wrong(self):
        self.sequential_statechart.remove_transition(Transition(source='a_state', target='b_state', event='event'))

        self.generic_test(self.sequential_statechart,
                          [Event('event')],
                          Then(ConsumeEvent('event'), InactiveState(state='a_state')),
                          False,
                          True)