From c745023137b7a6d84ac5eb37bde6c5a363767256 Mon Sep 17 00:00:00 2001 From: erick Date: Tue, 27 Oct 2009 03:19:16 +0000 Subject: Updated interface for Tutorial, Started writing tests to Tutorial --- diff --git a/tests/tutorialtests.py b/tests/tutorialtests.py index 864c452..ca70e11 100644 --- a/tests/tutorialtests.py +++ b/tests/tutorialtests.py @@ -79,6 +79,7 @@ class StateTest(unittest.TestCase): #### Action def test_add_dummy_action(self): action_name = self.state.add_action("action1") + assert action_name assert len(self.state.get_action_dict()) == 1 assert self.state.get_action_dict().has_key(action_name) assert self.state.get_action_dict()[action_name] == "action1" @@ -86,6 +87,7 @@ class StateTest(unittest.TestCase): def test_add_generate_unique_action_names(self): action_name1 = self.state.add_action("action1") action_name2 = self.state.add_action("action2") + assert action_name1 and action_name2 assert action_name1 != action_name2 def test_update_dummy_action(self): @@ -190,20 +192,220 @@ class StateTest(unittest.TestCase): assert transition_exists_error + class TutorialTest(unittest.TestCase): - """Test basic functionalities of tutorials""" + """Test tutorial functionality""" def setUp(self): - pass + self.tutorial = Tutorial("Tutorial Test") def tearDown(self): pass ######################### Basic interface cases ######################### + + #### Tutorial + def test_default_initial_value_in_tutorial(self): + assert len(self.tutorial.get_state_dict()) == 2 + assert set(self.tutorial.get_state_dict().keys()) == set([Tutorial._INIT,Tutorial._END]) + assert len(self.tutorial.get_action_dict()) == 0 + assert len(self.tutorial.get_transition_dict()) == 1 + assert self.tutorial.get_previous_states_dict(Tutorial._INIT).keys() == None + assert self.tutorial.get_following_states_dict(Tutorial._INIT).keys() == [Tutorial._END] + assert self.tutorial.get_previous_states_dict(Tutorial._END).keys() == [Tutorial._INIT] + assert self.tutorial.get_following_states_dict(Tutorial._END).keys() == None + + def test_initialize_tutorial_from_existing_dictionary(self): + # TODO: to be implemented + assert False + + #### State + def test_add_default_state(self): + state_name = self.tutorial.add_state() + assert state_name + assert len(self.tutorial.get_state_dict()) == 3 + assert set(self.tutorial.get_state_dict().keys()) == set([Tutorial._INIT,Tutorial._END, state_name]) + assert len(self.tutorial.get_action_dict()) == 0 + assert len(self.tutorial.get_transition_dict()) == 1 + + def test_add_state_with_action(self): + state_name = self.tutorial.add_state(action_list=["action1"]) + assert state_name + assert len(self.tutorial.get_state_dict()) == 3 + assert set(self.tutorial.get_state_dict().keys()) == set([Tutorial._INIT,Tutorial._END, state_name]) + assert len(self.tutorial.get_action_dict()) == 1 + assert len(self.tutorial.get_transition_dict()) == 1 + + def test_add_state_with_transition(self): + state_name = self.tutorial.add_state(transition_list=[("event1",Tutorial._END]) + assert state_name + assert len(self.tutorial.get_state_dict()) == 3 + assert set(self.tutorial.get_state_dict().keys()) == set([Tutorial._INIT,Tutorial._END, state_name]) + assert len(self.tutorial.get_action_dict()) == 1 + assert len(self.tutorial.get_transition_dict()) == 2 + + def test_add_generate_unique_state_names(self): + state_name1 = self.tutorial.add_state() + state_name2 = self.tutorial.add_state() + assert state_name1 and state_name2 + assert state_name1 != state_name2 + + def test_delete_lone_state(self): + state_name1 = self.tutorial.add_state() + self.tutorial.delete_state(state_name1) + assert len(self.tutorial.get_state_dict()) == 2 + assert set(self.tutorial.get_state_dict().keys()) == set([Tutorial._INIT,Tutorial._END]) + assert len(self.tutorial.get_action_dict()) == 0 + assert len(self.tutorial.get_transition_dict()) == 1 + assert self.tutorial.get_previous_states_dict(Tutorial._INIT).keys() == None + assert self.tutorial.get_following_states_dict(Tutorial._INIT).keys() == [Tutorial._END] + assert self.tutorial.get_previous_states_dict(Tutorial._END).keys() == [Tutorial._INIT] + assert self.tutorial.get_following_states_dict(Tutorial._END).keys() == None + + def test_delete_linked_state(self): + state_name1 = self.tutorial.add_state() + self.tutorial.update_transition(Tutorial._INITIAL_TRANSITION_NAME, \ + (Tutorial._AUTOMATIC_TRANSITION_EVENT, state_name1)) + transition_name1 = self.tutorial.add_transition(("event1", Tutorial._END)) + self.tutorial.delete_state(state_name1) + assert len(self.tutorial.get_state_dict()) == 2 + assert set(self.tutorial.get_state_dict().keys()) == set([Tutorial._INIT,Tutorial._END]) + assert len(self.tutorial.get_action_dict()) == 0 + assert len(self.tutorial.get_transition_dict()) == 1 + assert self.tutorial.get_previous_states_dict(Tutorial._INIT).keys() == None + assert self.tutorial.get_following_states_dict(Tutorial._INIT).keys() == [Tutorial._END] + assert self.tutorial.get_previous_states_dict(Tutorial._END).keys() == [Tutorial._INIT] + assert self.tutorial.get_following_states_dict(Tutorial._END).keys() == None + + #### Action + def test_add_dummy_action(self): + state_name = self.tutorial.add_state() + action_name = self.tutorial.add_action(state_name,"action1") + assert action_name + assert len(self.tutorial.get_action_dict()) == 1 + assert self.tutorial.get_action_dict().has_key(action_name) + assert self.tutorial.get_action_dict()[action_name] == "action1" + + def test_add_generate_unique_action_names(self): + state_name = self.tutorial.add_state() + action_name1 = self.state.add_action(state_name,"action1") + action_name2 = self.state.add_action(state_name,"action2") + assert action_name1 and action_name2 + assert action_name1 != action_name2 + + def test_update_dummy_action(self): + action_name = self.state.add_action("action1") + self.state.update_action(action_name, "action2") + assert len(self.state.get_action_dict()) == 1 + assert self.state.get_action_dict().has_key(action_name) + assert self.state.get_action_dict()[action_name] == "action2" + def test_delete_dummy_action(self): + action_name = self.state.add_action("action1") + assert len(self.state.get_action_dict()) == 1 + assert self.state.get_action_dict().has_key(action_name) + assert self.state.get_action_dict()[action_name] == "action1" + + self.state.delete_action(action_name) + assert len(self.state.get_action_dict()) == 0 + + def test_delete_all_dummy_actions(self): + action_name = self.state.add_action("action1") + assert len(self.state.get_action_dict()) == 1 + assert self.state.get_action_dict().has_key(action_name) + assert self.state.get_action_dict()[action_name] == "action1" + + self.state.delete_actions() + assert len(self.state.get_action_dict()) == 0 + + #### Transition + def test_add_dummy_transition(self): + transition_name = self.state.add_transition("transition1") + assert len(self.state.get_transition_dict()) == 1 + assert self.state.get_transition_dict().has_key(transition_name) + assert self.state.get_transition_dict()[transition_name] == "transition1" + + def test_add_generate_unique_transition_names(self): + transition_name1 = self.state.add_transition("transition1") + transition_name2 = self.state.add_transition("transition2") + assert transition_name1 != transition_name2 + + def test_update_dummy_transition(self): + transition_name = self.state.add_transition("transition1") + self.state.update_transition(transition_name, "transition2") + assert len(self.state.get_transition_dict()) == 1 + assert self.state.get_transition_dict().has_key(transition_name) + assert self.state.get_transition_dict()[transition_name] == "transition2" + + def test_delete_dummy_transition(self): + transition_name = self.state.add_transition("transition1") + assert len(self.state.get_transition_dict()) == 1 + assert self.state.get_transition_dict().has_key(transition_name) + assert self.state.get_transition_dict()[transition_name] == "transition1" + + self.state.delete_transition(transition_name) + assert len(self.state.get_transition_dict()) == 0 + + def test_delete_all_dummy_transitions(self): + transition_name = self.state.add_transition("transition1") + assert len(self.state.get_transition_dict()) == 1 + assert self.state.get_transition_dict().has_key(transition_name) + assert self.state.get_transition_dict()[transition_name] == "transition1" + + self.state.delete_transitions() + assert len(self.state.get_transition_dict()) == 0 + + + ######################### Limit cases ################################### + #### Tutorial + + #### State + + #### Action + + #### Transition ######################### Error cases ################################### + #### Tutorial + def test_initialize_tutorial_from_wrong_dictionary(self): + # TODO: to be implemented + assert False + + #### State + + #### Action + def test_update_unknown_action(self): + name_error = None + try: + self.state.update_action("unknown_name", "action") + except NameError, e: + name_error = e + + assert name_error + + + def test_delete_unknown_action(self): + name_error = None + try: + self.state.delete_action("unknown_name") + except NameError, e: + name_error = e + + assert name_error + + #### Transition + def test_add_existing_transition(self): + self.state.add_transition("transition") + transition_exists_error = None + try: + self.state.add_transition("transition") + except TransitionAlreadyExists, e: + transition_exists_error = e + + assert transition_exists_error + + if __name__ == "__main__": unittest.main() diff --git a/tutorius/tutorial.py b/tutorius/tutorial.py index a4da092..a902201 100644 --- a/tutorius/tutorial.py +++ b/tutorius/tutorial.py @@ -7,19 +7,24 @@ class Tutorial(object): _INIT = "INIT" _END = "END" + _INITIAL_TRANSITION_NAME = "Transition1" + _AUTOMATIC_TRANSITION_EVENT = "automatic" def __init__(self, name, state_dict=None): """ The constructor for the Tutorial. By default, the tutorial contains only an initial state and an end state. - The initial state doesn't contain any action or transition. - The end state doesn't contain any action either. + The initial state doesn't contain any action but it contains + a single automatic transition between the initial state and the + end state, named _INITIAL_TRANSITION. + The end state doesn't contain any action nor transition. If state_dict is provided, a valid initial state and an end state must be provided. @param name The name of the tutorial @param state_dict optional, a valid dictionary of states + @raise InvalidStateDictionary """ self.name = name @@ -54,13 +59,13 @@ class Tutorial(object): initialized with the action list and transition list and a new unique name is returned for this state. - The action is added using add_action. + The actions are added using add_action. - The transitions is added using add_transition. + The transitions are added using add_transition. @param action_list The list of valid actions for this state @param transition_list The list of valid transitions - @return string unique name for this state + @return unique name for this state """ name = self._generate_unique_state_name() @@ -162,47 +167,155 @@ class Tutorial(object): pointed to this one to the next state and remove all the unreachable states recursively. + All but the INIT and END states can be deleted. + @param state_name The name of the state to remove + @raise StateDeletionError when trying to delete the INIT or the END state """ pass - def get_actions(self, state_name): + def get_action_dict(self, state_name=None): """ + Returns a dictionary of all actions for a specific state. + If no state_name is provided, returns an action dictionary + containing actions for all states. + @param state_name The name of the state to list actions from - @return A list of actions for state_name + @return A dictionary of actions with action_name as key and action as value for state_name + @raise NameError if state_name doesn't exist """ pass - def get_events(self, state_name): + def get_transition_dict(self, state_name=None): """ + Returns a dictionary of all actions for a specific state. + If no state_name is provided, returns an action dictionary + containing actions for all states. + @param state_name The name of the state to list actions from - @return A list of events for state_name + @return A dictionary of transitions with transition_name as key and transition as value for state_name + @raise NameError if state_name doesn't exist + """ + pass + + def get_state_dict(self): + """ + @return A dictionary of all the states in the tutorial with state_name as key and state as value """ pass - def get_following_states(self, state_name): + def get_following_states_dict(self, state_name): """ - Returns a tuple of the names of the states that point to the given - state. If there is no such state, the function raises a KeyError. + Returns a dictionary of the states that are immediately reachable from + a specific state. - @param state_name The name of the state to analyse - @raise KeyError When there is no state by this name in the FSM + @param state_name The name of the state + @raise NameError if state_name doesn't exist """ pass - def get_previous_states(self, state_name): + def get_previous_states_dict(self, state_name): """ - Returns a tuple of the names of the state that can transition to - the given state. If there is no such state, the function raises a - KeyError. + Returns a dictionary of the states that can transition to a + specific state. - @param state_name The name of the state that the returned states might - transition to. - @raise KeyError When there is no state by this name in the FSM + @param state_name The name of the state + @raise NameError if state_name doesn't exist """ pass + # Convenience methods for common tutorial manipulations + def add_state_before(self, state_name, action_list=[], event_list=[]): + """ + Add a new state just before another state state_name. All transitions + going to state_name are updated to end on the new state and all + events will be converted to transitions ending on state_name. + + When event_list is empty, an automatic transition to state_name + will be added to maintain consistency. + + @param state_name The name of the state that will be preceded by the + new state + @param action_list The list of valid actions for this state + @param event_list The list of events that will be converted to transitions to state_name + @return unique name for this state + @raise NameError if state_name doesn't exist + """ + pass + + # Callback mecanism to allow automatic change notification when + # the tutorial is modified + def register_action_added_cb(self, cb): + """ + Register a function cb that will be called when any action from + the tutorial is added. + + cb should be of the form: + + cb(action_name, new_action) where: + action_name is the unique name of the action that was added + new_action is the new action + + @param cb The callback function to be called + @raise InvalidCallbackFunction if the callback has less or more than + 2 arguments + """ + pass + + def register_action_updated_cb(self, cb): + """ + Register a function cb that will be called when any action from + the tutorial is updated. + + cb should be of the form: + + cb(action_name, new_action) where: + action_name is the unique name of the action that has changed + new_action is the new action that replaces the old one + + @param cb The callback function to be called + @raise InvalidCallbackFunction if the callback has less or more than + 2 arguments + """ + pass + + def register_action_deleted_cb(self, cb): + """ + Register a function cb that will be called when any action from + the tutorial is deleted. + + cb should be of the form: + + cb(action_name, old_action) where: + action_name is the unique name of the action that was deleted + old_action is the new action that replaces the old one + + @param cb The callback function to be called + @raise InvalidCallbackFunction if the callback has less or more than + 2 arguments + """ + pass + + def register_transition_updated_cb(self, cb): + """ + Register a function cb that will be called when any transition from + the tutorial is updated. + + cb should be of the form: + + cb(transition_name, new_transition) where: + transition_name is the unique name of the transition + that has changed + new_transition is the new transition that replaces the old one + + @param cb The callback function to be called + @raise InvalidCallbackFunction if the callback has less or more than + 2 arguments + """ + pass + + # Validation to assert precondition def _validate_action(self, action): """ Validate that an action conforms to what we expect, @@ -465,3 +578,15 @@ class TransitionAlreadyExists(Exception): pass +class InvalidStateDictionary(Exception): + """ + Raised when an initialization dictionary could not be used to initialize + a tutorial + """ + pass + +class StateDeletionError(Exception): + """ + Raised when trying to delete an INIT or an END state from a tutorial + """ + pass -- cgit v0.9.1