def test_with_expression_a(self):
        spm = StatePathMatcher(expression='A')

        self.assertTrue(spm.match('A'))
        self.assertTrue(spm.match('B.A'))
        self.assertFalse(spm.match('B'))
        self.assertFalse(spm.match('A.B'))
    def test_with_expression_self_dot_a_dot_b(self):
        spm = StatePathMatcher(expression='self.A.B')

        spm.state = state_1

        self.assertTrue(spm.match('A.B'))

        self.assertFalse(spm.match('A'))
        self.assertFalse(spm.match('B'))
    def test_with_expression_self_dot_a_dot_b(self):
        spm = StatePathMatcher(expression='self.A.B')

        spm.state = state_1

        self.assertTrue(spm.match('A.B'))

        self.assertFalse(spm.match('A'))
        self.assertFalse(spm.match('B'))
    def test_with_expression_a_dot_b_tilde_c_dot_d(self):
        spm = StatePathMatcher(expression='A.B~C.D')

        self.assertTrue(spm.match('A.B.C.D'))
        self.assertTrue(spm.match('A.B.X.C.D'))
        self.assertTrue(spm.match('A.B.X.Y.C.D'))
        self.assertTrue(spm.match('Z.A.B.X.Y.C.D'))

        self.assertFalse(spm.match('A.B.C.D.X'))
        self.assertFalse(spm.match('B.C.D'))
        self.assertFalse(spm.match('A.B.C'))
        self.assertFalse(spm.match('A.B.D'))
        self.assertFalse(spm.match('A.C.D'))
        self.assertFalse(spm.match('A.B.Y.C.D.X'))
    def test_with_expression_a_tilde_b(self):
        spm = StatePathMatcher(expression='A~B')

        self.assertTrue(spm.match('A.B'))
        self.assertTrue(spm.match('A.X.B'))
        self.assertTrue(spm.match('A.X.Y.B'))
        self.assertTrue(spm.match('X.A.B'))
        self.assertTrue(spm.match('X.A.Y.B'))

        self.assertFalse(spm.match('B'))
        self.assertFalse(spm.match('A'))
        self.assertFalse(spm.match('A.B.X'))
        self.assertFalse(spm.match('A.Y.B.X'))
        self.assertFalse(spm.match('Y.A.B.X'))
    def test_with_expression_a_dot_self_dot_a_dot_b_dot_c(self):

        with self.assertRaises(Exception) as cm:
            spm = StatePathMatcher(expression='A.self.A.B.C')

        msg = "Invalid use of 'self' at part 1"
        self.assertEqual(str(cm.exception), msg)
    def test_with_expression_a_tilde_b_tilde_c(self):

        with self.assertRaises(Exception) as cm:
            spm = StatePathMatcher(expression='A~B~C')

        msg = "Invalid use of '~' at part 0"
        self.assertEqual(str(cm.exception), msg)
    def test_with_expression_a_dot_b_tilde_c_dot_d(self):
        spm = StatePathMatcher(expression='A.B~C.D')

        self.assertTrue(spm.match('A.B.C.D'))
        self.assertTrue(spm.match('A.B.X.C.D'))
        self.assertTrue(spm.match('A.B.X.Y.C.D'))
        self.assertTrue(spm.match('Z.A.B.X.Y.C.D'))

        self.assertFalse(spm.match('A.B.C.D.X'))
        self.assertFalse(spm.match('B.C.D'))
        self.assertFalse(spm.match('A.B.C'))
        self.assertFalse(spm.match('A.B.D'))
        self.assertFalse(spm.match('A.C.D'))
        self.assertFalse(spm.match('A.B.Y.C.D.X'))
    def test_with_expression_a_tilde_b(self):
        spm = StatePathMatcher(expression='A~B')

        self.assertTrue(spm.match('A.B'))
        self.assertTrue(spm.match('A.X.B'))
        self.assertTrue(spm.match('A.X.Y.B'))
        self.assertTrue(spm.match('X.A.B'))
        self.assertTrue(spm.match('X.A.Y.B'))

        self.assertFalse(spm.match('B'))
        self.assertFalse(spm.match('A'))
        self.assertFalse(spm.match('A.B.X'))
        self.assertFalse(spm.match('A.Y.B.X'))
        self.assertFalse(spm.match('Y.A.B.X'))
    def test_with_expression_a_dot_b(self):
        spm = StatePathMatcher(expression='A.B')

        self.assertTrue(spm.match('A.B'))
        self.assertTrue(spm.match('X.A.B'))

        self.assertFalse(spm.match('B'))
        self.assertFalse(spm.match('A'))
        self.assertFalse(spm.match('B.A'))
        self.assertFalse(spm.match('A.B.X'))
Esempio n. 11
0
    def get_substate(self, value, callback=None):
        '''Used to get a substate of this state that matches a given value.

           If the value is a state object, then the value will be returned if
           it is indeed a substate of this state, otherwise null is returned.

           If the given value is a string, then the string is assumed to be a
           path expression to a substate. The value is then parsed to find the
           closest match. For path expression syntax, refer to the {@link
           StatePathMatcher} class.

           If there is no match then null is returned. If there is more than
           one match then null is returned and an error is generated indicating
           ambiguity of the given value.

           An optional callback can be provided to handle the scenario when
           either no substate is found or there is more than one match. The
           callback is then given the opportunity to further handle the outcome
           and return a result which the get_substate method will then return.
           The callback should have the following signature::

               function(state, value, paths)

               state: The state on which get_state() was invokedon.
               value: The value supplied to get_state().
               paths: An array of substate paths that matched the given value.

           If there were no matches then `paths` is not provided to the
           callback.

           Parameters:

           * value {State|String} - Used to identify a substate of this state.
           * callback {Function} - Optional callback.
        '''

        if value is None:
            return None

        if not isinstance(value, basestring):
            if value in self._registered_substates:
                return value
            elif isinstance(value, State):
                return None
            else:
                msg = ("Cannot find matching substate. value must be a State "
                       "class or string, not type: {0}").format(type(value))
                self.state_log_error(msg)
                raise Exception(msg)

        # [PORT] Considered this, but it seemed to match on what should
        #        remain ambiguous.
        #for state in self._registered_substates:
            #if value == state.name:
                #return state

        # Not found yet, so keep looking with path matcher.
        matcher = StatePathMatcher(state=self, expression=value)

        matches = []

        # Grab the paths associated with this state name.
        paths = self._registered_substate_paths[matcher.last_part] \
                if matcher.last_part in self._registered_substate_paths \
                else None

        if paths is None:
            return self._notify_substate_not_found(callback=callback,
                                                   value=value)

        if value in paths:
            matches.append(paths[value])
        else:
            for path in paths:
                if matcher.match(path):
                    matches.append(paths[path])

        if len(matches) == 1:
            return matches[0]

        if len(matches) > 1:
            path_keys = []
            for key in paths:
                path_keys.append(key)

            if callback is not None:
                return self._notify_substate_not_found(callback=callback,
                                                       value=value,
                                                       keys=path_keys)

            msg = ("Cannot find substate matching '{0}' in state {1}. "
                   "Ambiguous with "
                   "the following: {2}").format(value, self.full_path,
                                                ', '.join(path_keys))
            self.state_log_error(msg)
            raise Exception(msg)

        return self._notify_substate_not_found(callback=callback,
                                               value=value)
    def test_with_bad_match_arguments(self):
        spm = StatePathMatcher(expression='self.A~B')

        self.assertFalse(spm.match(None))
        self.assertFalse(spm.match(''))
        self.assertFalse(spm.match(dict))
Esempio n. 13
0
    def get_substate(self, value, callback=None):
        '''Used to get a substate of this state that matches a given value.

           If the value is a state object, then the value will be returned if
           it is indeed a substate of this state, otherwise null is returned.

           If the given value is a string, then the string is assumed to be a
           path expression to a substate. The value is then parsed to find the
           closest match. For path expression syntax, refer to the {@link
           StatePathMatcher} class.

           If there is no match then null is returned. If there is more than
           one match then null is returned and an error is generated indicating
           ambiguity of the given value.

           An optional callback can be provided to handle the scenario when
           either no substate is found or there is more than one match. The
           callback is then given the opportunity to further handle the outcome
           and return a result which the get_substate method will then return.
           The callback should have the following signature::

               function(state, value, paths)

               state: The state on which get_state() was invokedon.
               value: The value supplied to get_state().
               paths: An array of substate paths that matched the given value.

           If there were no matches then `paths` is not provided to the
           callback.

           Parameters:

           * value {State|String} - Used to identify a substate of this state.
           * callback {Function} - Optional callback.
        '''

        if value is None:
            return None

        if not isinstance(value, basestring):
            if value in self._registered_substates:
                return value
            elif isinstance(value, State):
                return None
            else:
                msg = ("Cannot find matching substate. value must be a State "
                       "class or string, not type: {0}").format(type(value))
                self.state_log_error(msg)
                raise Exception(msg)

        # [PORT] Considered this, but it seemed to match on what should
        #        remain ambiguous.
        #for state in self._registered_substates:
        #if value == state.name:
        #return state

        # Not found yet, so keep looking with path matcher.
        matcher = StatePathMatcher(state=self, expression=value)

        matches = []

        # Grab the paths associated with this state name.
        paths = self._registered_substate_paths[matcher.last_part] \
                if matcher.last_part in self._registered_substate_paths \
                else None

        if paths is None:
            return self._notify_substate_not_found(callback=callback,
                                                   value=value)

        if value in paths:
            matches.append(paths[value])
        else:
            for path in paths:
                if matcher.match(path):
                    matches.append(paths[path])

        if len(matches) == 1:
            return matches[0]

        if len(matches) > 1:
            path_keys = []
            for key in paths:
                path_keys.append(key)

            if callback is not None:
                return self._notify_substate_not_found(callback=callback,
                                                       value=value,
                                                       keys=path_keys)

            msg = ("Cannot find substate matching '{0}' in state {1}. "
                   "Ambiguous with "
                   "the following: {2}").format(value, self.full_path,
                                                ', '.join(path_keys))
            self.state_log_error(msg)
            raise Exception(msg)

        return self._notify_substate_not_found(callback=callback, value=value)
    def test_with_bad_match_arguments(self):
        spm = StatePathMatcher(expression='self.A~B')

        self.assertFalse(spm.match(None))
        self.assertFalse(spm.match(''))
        self.assertFalse(spm.match(dict))