Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/tutorialtests.py206
-rw-r--r--tutorius/tutorial.py167
2 files changed, 350 insertions, 23 deletions
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