def test_undefined_state_is_invalid_if_no_initial_state(self): fsm = SimpleFSM({'transitions': {'YIN': {'YANG'}, 'YANG': {}}}) with self.assertRaises(InvalidStateError) as expected: fsm.change_to('FEN') self.assertEqual( "Cannot change state. 'FEN' is not a valid state.", str(expected.exception) )
def test_transition_state_is_not_valid(self): with self.assertRaises(InconsistentModelError) as expected: self.fsm = SimpleFSM({ 'transitions': { 'defined': {'created'}, } }) self.assertEqual( "Transition definition references non-existent state: 'created'.", str(expected.exception) )
def setUp(self): self.fsm = SimpleFSM({ 'initial': 'defined', 'transitions': { 'defined': {'created'}, 'created': {'configured'}, 'configured': {'active'}, 'active': {'destroyed', 'error'}, 'destroyed': {}, 'error': {'configured', 'active', 'destroyed'}, } })
def __init__(self, serial_instance): self._serial = serial_instance self._sm = SimpleFSM({ 'initial': 'idle', 'transitions': { 'idle': {'sending'}, 'sending': {'wait_response', 'success'}, 'wait_response': {'resending', 'check_response'}, 'check_response': {'resending', 'wait_response', 'success'}, 'resending': {'wait_response', 'failure'}, 'success': {'idle', 'sending'}, 'failure': {'idle', 'sending'}, } }) self.clear()
def test_any_defined_state_is_valid_if_no_initial_state(self): fsm = SimpleFSM({'transitions': {'YIN': {'YANG'}, 'YANG': {}}}) fsm.change_to('YANG') self.assertEqual('YANG', fsm.current)
class TestSimpleFSM(unittest.TestCase): def setUp(self): self.fsm = SimpleFSM({ 'initial': 'defined', 'transitions': { 'defined': {'created'}, 'created': {'configured'}, 'configured': {'active'}, 'active': {'destroyed', 'error'}, 'destroyed': {}, 'error': {'configured', 'active', 'destroyed'}, } }) def test_empty_model_raises_exception(self): with self.assertRaises(ValueError) as expected: # pylint: disable=W0612 SimpleFSM({}) self.assertEqual( 'The passed-in state model cannot by empty or None.', str(expected.exception) ) def test_none_model_raises_exception(self): with self.assertRaises(ValueError) as expected: # pylint: disable=W0612 SimpleFSM(None) self.assertEqual( 'The passed-in state model cannot by empty or None.', str(expected.exception) ) def test_fsm_with_no_initial_state(self): fsm = SimpleFSM({'transitions': {'YIN': {'YANG'}, 'YANG': {}}}) self.assertEqual(None, fsm.current) def test_any_defined_state_is_valid_if_no_initial_state(self): fsm = SimpleFSM({'transitions': {'YIN': {'YANG'}, 'YANG': {}}}) fsm.change_to('YANG') self.assertEqual('YANG', fsm.current) def test_undefined_state_is_invalid_if_no_initial_state(self): fsm = SimpleFSM({'transitions': {'YIN': {'YANG'}, 'YANG': {}}}) with self.assertRaises(InvalidStateError) as expected: fsm.change_to('FEN') self.assertEqual( "Cannot change state. 'FEN' is not a valid state.", str(expected.exception) ) def test_initial_state_defined(self): self.assertEqual('defined', self.fsm.current) def test_no_match_in_state_model_for_initial_state_raises_exception(self): with self.assertRaises(InconsistentModelError) as expected: # pylint: disable=W0612 SimpleFSM({ 'initial': 'oranges', 'transitions': { 'apples': {'pomegranates', 'ugli fruit'}, 'pomegranates': {'apples'}, } }) self.assertEqual( "Initial state 'oranges' does not match any valid state.", str(expected.exception) ) def test_check_for_a_valid_state_change(self): self.assertTrue(self.fsm.permitted('created')) self.assertFalse(self.fsm.restricted('created')) def test_check_for_an_invalid_state_change(self): self.fsm.change_to('created') self.assertTrue(self.fsm.restricted('active')) self.assertFalse(self.fsm.permitted('active')) def test_change_to_a_valid_state(self): self.fsm.change_to('created') self.assertEqual('created', self.fsm.current) def test_attempt_to_change_to_invalid_state_raises_exception(self): with self.assertRaises(InvalidStateError) as expected: self.fsm.change_to('configured') self.assertEqual( "Attempted change to state 'configured' is not permitted.", str(expected.exception) ) def test_force_change_with_no_attempted_change_to_raises_exception(self): with self.assertRaises(MethodNotAllowedError) as expected: self.fsm.force_change_to('active') self.assertEqual( "'force_change_to' can only be called after a " "failed call to 'change_to' for the same state.", str(expected.exception) ) def test_force_change_to_allowed_after_attempted_change_to(self): try: self.fsm.change_to('active') except InvalidStateError: self.fsm.force_change_to('active') self.assertEqual('active', self.fsm.current) def test_future_state_is_attainable(self): self.assertTrue(self.fsm.reachable('destroyed')) def test_future_state_is_not_attainable(self): self.fsm.change_to('created') self.assertFalse(self.fsm.reachable('defined')) def test_transition_state_is_not_valid(self): with self.assertRaises(InconsistentModelError) as expected: self.fsm = SimpleFSM({ 'transitions': { 'defined': {'created'}, } }) self.assertEqual( "Transition definition references non-existent state: 'created'.", str(expected.exception) ) def test_multiple_transition_states_not_valid(self): with self.assertRaises(InconsistentModelError) as expected: self.fsm = SimpleFSM({ 'transitions': { 'defined': {'created'}, 'active': {'deleted'}, } }) self.assertEqual( "Transition definition references non-existent " "states: 'created', 'deleted'.", str(expected.exception) )
class Tx(): def __init__(self, serial_instance): self._serial = serial_instance self._sm = SimpleFSM({ 'initial': 'idle', 'transitions': { 'idle': {'sending'}, 'sending': {'wait_response', 'success'}, 'wait_response': {'resending', 'check_response'}, 'check_response': {'resending', 'wait_response', 'success'}, 'resending': {'wait_response', 'failure'}, 'success': {'idle', 'sending'}, 'failure': {'idle', 'sending'}, } }) self.clear() def clear(self): self.busy = False self.resp = None self._ba = [] try: if self._sm.current is not 'idle': self._sm.change_to('idle') except exceptions.InvalidStateError: self._sm.force_change_to('idle') def send(self, packet): if self.busy: return False self._sm.change_to('sending') command, payload = packet p = Protocol() self._ba = bytearray(p.build(command, payload)) self._serial.write(self._ba) if AAE_COMMAND[command][1] is None: self._sm.change_to('success') return True self.tx_cmd = command self._sm.change_to('wait_response') self.start = time.clock() self.resend_cnt = 0 self.busy = True # not allow another command return True def get_response(self, command, resp): if self._sm.current is not 'wait_response': return self.rx_cmd, self.resp = command, resp if (debug): print('Tx.get_response', command, self.resp), self._sm.change_to('check_response') def exec_(self): if not self.busy: return #First Decision Table if self._sm.current is 'wait_response': t1 = time.clock() - self.start if (t1 > 2): self._sm.change_to('resending') print('sm', self._sm.current), elif self._sm.current is 'check_response': if self.rx_cmd is not self.tx_cmd: self._sm.change_to('wait_response') return if self.resp is not None: self._sm.change_to('success') else: self._sm.change_to('wait_response') elif self._sm.current is 'success' or self._sm.current is 'failure': if self.resp is None: pass #print('judge', self._sm.current, self.tx_cmd) elif isinstance(self.resp, bool) or isinstance(self.resp, int): pass #print('judge', self._sm.current, self.resp) else: # Should be list or tuple if len(self.resp) < 1: resp = '' elif isinstance(self.resp[0], int): resp = ''.join('{0:02x}'.format(b) for b in self.resp) else: # Should be list or tuple in sub-item resp = '' for lst in self.resp: resp += ''.join('{0:02x}'.format(b) for b in lst) resp += '|' #print('judge', self._sm.current, self.tx_cmd, resp) self.last_result = self._sm.current self._sm.change_to('idle') self.busy = False # Second Decision Table (have to do immediately) if self._sm.current is 'resending': if self.resend_cnt > 0: self._sm.change_to('failure') else: self._serial.write(self._ba) self.resend_cnt += 1 self._sm.change_to('wait_response') last_result = 'success'