Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormike <michael.jmontcalm@gmail.com>2009-04-26 21:42:20 (GMT)
committer mike <michael.jmontcalm@gmail.com>2009-04-26 21:42:20 (GMT)
commit2a1c6d6fbcb60c44d8690add635260faf243dcd6 (patch)
tree25b10b497c515416370cbc0dd86e05bd6a2f4155
parenta4114a946cc7a57c1cfce5760737c3f05425bc86 (diff)
parente784a2f0b92ecaf46a77ddca94b31fcc86e0cbae (diff)
(LP 352437) Core : Completed basic serializer interactions with core
(actions, event filters) - needs tests Also, merged branch 'mike' into jc_support Conflicts: source/external/source/sugar-toolkit/src/sugar/tutorius/actions.py source/external/source/sugar-toolkit/src/sugar/tutorius/bundler.py source/external/source/sugar-toolkit/src/sugar/tutorius/tests/run-tests.py source/external/source/sugar-toolkit/src/sugar/tutorius/tests/serializertests.py
-rw-r--r--src/sugar/tutorius/Makefile.am1
-rw-r--r--src/sugar/tutorius/actions.py4
-rw-r--r--src/sugar/tutorius/bundler.py150
-rw-r--r--src/sugar/tutorius/constraints.py26
-rw-r--r--src/sugar/tutorius/core.py14
-rw-r--r--src/sugar/tutorius/properties.py12
-rw-r--r--src/sugar/tutorius/tests/actiontests.py79
-rw-r--r--src/sugar/tutorius/tests/constraintstests.py38
-rw-r--r--src/sugar/tutorius/tests/coretests.py41
-rw-r--r--src/sugar/tutorius/tests/propertiestests.py40
-rwxr-xr-xsrc/sugar/tutorius/tests/run-tests.py1
-rw-r--r--src/sugar/tutorius/tests/serializertests.py29
12 files changed, 324 insertions, 111 deletions
diff --git a/src/sugar/tutorius/Makefile.am b/src/sugar/tutorius/Makefile.am
index 0632e47..7223c60 100644
--- a/src/sugar/tutorius/Makefile.am
+++ b/src/sugar/tutorius/Makefile.am
@@ -14,5 +14,4 @@ sugar_PYTHON = \
linear_creator.py \
constraints.py \
properties.py \
- tutoserialize.py \
bundler.py
diff --git a/src/sugar/tutorius/actions.py b/src/sugar/tutorius/actions.py
index 2d630be..d2516f7 100644
--- a/src/sugar/tutorius/actions.py
+++ b/src/sugar/tutorius/actions.py
@@ -135,9 +135,9 @@ class BubbleMessage(Action):
Action.__init__(self)
self.message = TStringProperty(message)
# Create the position as an array of fixed-size 2
- self.position = TArrayProperty(pos, 2, 2)
+ self.position = TArrayProperty(pos or [0,0], 2, 2)
# Do the same for the tail position
- self.tail_pos = TArrayProperty(tailpos, 2, 2)
+ self.tail_pos = TArrayProperty(tailpos or [0,0], 2, 2)
self.overlay = None
self._bubble = None
diff --git a/src/sugar/tutorius/bundler.py b/src/sugar/tutorius/bundler.py
index 58288ca..34b3a12 100644
--- a/src/sugar/tutorius/bundler.py
+++ b/src/sugar/tutorius/bundler.py
@@ -140,33 +140,38 @@ class XMLSerializer(Serializer):
for action in action_list:
actionNode = doc.createElement("Action")
actionsList.appendChild(actionNode)
+ # Using .__class__ since type() doesn't have the same behavior
+ # with class derivating from object and class that don't
+ actionNode.setAttribute("Class", str(action.__class__))
+
if type(action) is DialogMessage:
- # Using .__class__ since type() doesn't have the same behavior
- # with class derivating from object and class that don't
- actionNode.setAttribute("Class", str(action.__class__))
- actionNode.setAttribute("Message", action.message)
- actionNode.setAttribute("Position", action.position)
+ actionNode.setAttribute("Message", action.message.value)
+ actionNode.setAttribute("PositionX", action.position.value[0])
+ actionNode.setAttribute("PositionY", str(action.position.value[1]))
elif type(action) is BubbleMessage:
- actionNode.setAttribute("Class", str(action.__class__))
- actionNode.setAttribute("Message", action.message)
- actionNode.setAttribute("Position", str(action.position))
- actionNode.setAttribute("Tail_pos", str(action.tail_pos))
+ actionNode.setAttribute("Message", action.message.value)
+ actionNode.setAttribute("PositionX", str(action.position.value[0]))
+ actionNode.setAttribute("PositionY", str(action.position.value[1]))
+ actionNode.setAttribute("Tail_posX", str(action.tail_pos.value[0]))
+ actionNode.setAttribute("Tail_posY", str(action.tail_pos.value[1]))
# TODO : elif for each type of action
elif type(action) is WidgetIdentifyAction:
- actionNode.setAttribute("Class", str(action.__class__))
- # TODO
+ # Nothing else to save
+ pass
elif type(action) is ChainAction:
- # TODO
- actionNode.setAttribute("Class", str(action.__class__))
+ # Recusively write the contained actions - in the correct order
+ subActionsNode = self._create_actions_node(action._actions, doc)
+ actionNode.appendChild(subActionsNode)
elif type(action) is DisableWidgetAction:
- # TODO
- actionNode.setAttribute("Class", str(action.__class__))
+ # Remember the target
+ actionNode.setAttribute("Target", action._target)
elif type(action) is TypeTextAction:
- # TODO
- actionNode.setAttribute("Class", str(action.__class__))
+ # Save the text and the widget
+ actionNode.setAttribute("Widget", action._widget)
+ actionNode.setAttribute("Text", action._text)
elif type(action) is ClickAction:
- # TODO
- actionNode.setAttribute("Class", str(action.__class__))
+ # Save the widget to click
+ actionNode.setAttribute("Widget", action._widget)
return actionsList
@@ -177,19 +182,27 @@ class XMLSerializer(Serializer):
eventFiltersList = doc.createElement("EventFiltersList")
for event_f in event_filters:
eventFilterNode = eventFiltersList.appendChild("EventFilter")
- # TODO : elif for each type of event filters
+
+ # Using .__class__ since type() doesn't have the same behavior
+ # with class derivating from object and class that don't
+ eventFilterNode.setAttribute("Class", str(event_f.__class__))
+
+ # Write the name of the next state
+ eventFilterNode.setAttribute("NextState", event_f.next_state)
+
if type(event_f) is sugar.tutorius.filters.TimerEvent:
- # Using .__class__ since type() doesn't have the same behavior
- # with class derivating from object and class that don't
+ eventFilterNode.setAttribute("Timeout_s", str(event_f._timeout))
- # TODO
- eventFilterNode.setAttribute("Class", str(event_f.__class__))
elif type(event_f) is sugar.tutorius.filters.GtkWidgetEventFilter:
- # TODO
- eventFilterNode.setAttribute("Class", str(event_f.__class__))
+ eventFilterNode.setAttribute("EventName", event_f._event_name)
+ eventFilterNode.setAttribute("ObjectId", event_f._object_id)
+
elif type(event_f) is sugar.tutorius.filters.GtkWidgetTypeFilter:
- # TODO
- eventFilterNode.setAttribute("Class", str(event_f.__class__))
+ eventFilterNode.setAttribute("ObjectId", event_f._object_id)
+ if event_f._strokes is not None:
+ eventFilterNode.setAttribute("Strokes", event_f._strokes)
+ if event_f._text is not None:
+ eventFilterNode.setAttribute("Text", event_f._text)
return eventFiltersList
@@ -204,14 +217,15 @@ class XMLSerializer(Serializer):
fsm_element.setAttribute("Name", fsm.name)
fsm_element.setAttribute("StartStateName", fsm.start_state_name)
statesDict = fsm_element.appendChild(self._create_state_dict_node(fsm._states, doc))
- actionsList = fsm_element.appendChild(self._create_action_list_node(fsm.actions, doc))
+
+ fsm_actions_node = self._create_action_list_node(fsm.actions, doc)
+ fsm_actions_node.tagName = "FSMActions"
+ actionsList = fsm_element.appendChild(fsm_actions_node)
file_object = open(os.path.join(path, xml_filename), "w")
- # Commented for DEBUG purposes
- #xml.dom.ext.PrettyPrint(doc, file_object)
file_object.write(doc.toprettyxml())
file_object.close()
-
+
def _find_tutorial_dir_with_guid(self, guid):
"""
@@ -284,18 +298,34 @@ class XMLSerializer(Serializer):
# item(0) because there is always only one <EventFilterList> tag in the xml file
# so states_elem should always contain only one element
event_filter_element_list = filters_elem.item(0).getElementsByTagName("EventFilter")
+ new_event_filter = None
for event_filter in event_filter_element_list:
- # TODO : elif for each type of event filter
+ # Load the name of the next state for this filter
+ next_state = event_filter.getAttribute("NextState")
+
if event_filter.getAttribute("Class") == str(sugar.tutorius.filters.TimerEvent):
- # TODO
- pass
+ timeout = int(event_filter.getAttribute("Timeout_s"))
+ new_event_filter = TimerEvent(next_state, timeout)
+
elif event_filter.getAttribute("Class") == str(sugar.tutorius.filters.GtkWidgetEventFilter):
- # TODO
- pass
+ # Get the event name and the object's ID
+ event_name = event_filter.getAttribute("EventName")
+ object_id = event_filter.getAttribute("ObjectId")
+
+ new_event_filter = GtkWidgetEventFilter(next_state, object_id, event_name)
elif event_filter.getAttribute("Class") == str(sugar.tutorius.filters.GtkWidgetTypeFilter):
- # TODO
- pass
+ # Get the widget to write in and the text
+ object_id = event_filter.getAttribute("ObjectId")
+ if event_filter.hasAttribute("Text"):
+ text = event_filter.getAttribute("Text")
+ new_event_filter = GtkWidgetTypeFilter(next_state, object_id, text=text)
+ elif event_filter.hasAttribute("Strokes"):
+ strokes = event_filter.getAttribute("Strokes")
+ new_event_filter = GtkWidgetTypeFilter(next_state, object_id, strokes=strokes)
+
+ if new_event_filter is not None:
+ reformed_event_filter.append(new_event_filter)
return reformed_event_filters_list
@@ -314,28 +344,40 @@ class XMLSerializer(Serializer):
# TODO : elif for each type of action
if action.getAttribute("Class") == str(DialogMessage):
message = action.getAttribute("Message")
- position = action.getAttribute("Postion")
+ positionX = int(action.getAttribute("PositionX"))
+ positionY = int(action.getAttribute("PositionY"))
+ position = [positionX, positionY]
reformed_actions_list.append(DialogMessage(message,position))
elif action.getAttribute("Class") == str(BubbleMessage):
message = action.getAttribute("Message")
- position = action.getAttribute("Postion")
- tail_pos = action.getAttribute("Tail_pos")
+ positionX = int(action.getAttribute("PositionX"))
+ positionY = int(action.getAttribute("PositionY"))
+ position = [positionX, positionY]
+ tail_posX = action.getAttribute("Tail_posX")
+ tail_posY = action.getAttribute("Tail_posY")
+ tail_pos = [tail_posX, tail_posY]
reformed_actions_list.append(BubbleMessage(message,position,None,tail_pos))
elif action.getAttribute("Class") == str(WidgetIdentifyAction):
- # TODO
- pass
+ reformed_actions_list.append(WidgetIdentifyAction())
elif action.getAttribute("Class") == str(ChainAction):
- # TODO
- pass
+ # Load the subactions
+ subActionsList = _load_xml_actions(action.getElementsByTagName("Actions"))
+ reformed_actions_list.append(ChainAction(subActionsList))
elif action.getAttribute("Class") == str(DisableWidgetAction):
- # TODO
- pass
+ # Get the target
+ targetName = action.getAttribute("Target")
+ reformed_actions_list.append(DisableWidgetAction(targetName))
elif action.getAttribute("Class") == str(TypeTextAction):
- # TODO
- pass
+ # Get the widget and the text to type
+ widget = action.getAttribute("Widget")
+ text = action.getAttribute("Text")
+
+ reformed_actions_list.append(TypeTextAction(widget, text))
elif action.getAttribute("Class") == str(ClickAction):
- # TODO
- pass
+ # Load the widget to click
+ widget = action.getAttribute("Widget")
+
+ reformed_actions_list.append(ClickAction(widget))
return reformed_actions_list
@@ -383,7 +425,7 @@ class XMLSerializer(Serializer):
fsm.add_state(state)
# Load the actions on this FSM
- actions = self._load_xml_actions(fsm_elem.getElementsByTagName("Actions"))
+ actions = self._load_xml_actions(fsm_elem.getElementsByTagName("FSMActions"))
for action in actions:
fsm.add_action(action)
@@ -399,7 +441,6 @@ class XMLSerializer(Serializer):
"""
Load fsm from xml file whose .ini file guid match argument guid.
"""
-
# Fetch the directory (if any)
tutorial_dir = self._find_tutorial_dir_with_guid(guid)
@@ -421,7 +462,6 @@ class TutorialBundler:
def __init__(self,generated_guid = None):
"""
- TODO.
Tutorial_bundler constructor. If a GUID is given in the parameter, the
Tutorial_bundler object will be associated with it. If no GUID is given,
a new GUID will be generated,
diff --git a/src/sugar/tutorius/constraints.py b/src/sugar/tutorius/constraints.py
index a666ecb..0e09664 100644
--- a/src/sugar/tutorius/constraints.py
+++ b/src/sugar/tutorius/constraints.py
@@ -81,10 +81,10 @@ class LowerLimitConstraint(ValueConstraint):
raise LowerLimitConstraintError()
return
-class SizeConstraintError(Exception):
+class MaxSizeConstraintError(Exception):
pass
-class SizeConstraint(ValueConstraint):
+class MaxSizeConstraint(ValueConstraint):
def validate(self, value):
"""
Evaluate whether a given object is smaller than the given size when
@@ -94,9 +94,27 @@ class SizeConstraint(ValueConstraint):
bigger than the limit.
"""
if self.limit is not None:
- if self.limit > len(value):
+ if self.limit >= len(value):
return
- raise SizeConstraintError("Setter : trying to set value of length %d while limit is %d"%(len(value), self.limit))
+ raise MaxSizeConstraintError("Setter : trying to set value of length %d while limit is %d"%(len(value), self.limit))
+ return
+
+class MinSizeConstraintError(Exception):
+ pass
+
+class MinSizeConstraint(ValueConstraint):
+ def validate(self, value):
+ """
+ Evaluate whether a given object is smaller than the given size when
+ run through len(). Great for string, lists and the like. ;)
+
+ @raise SizeConstraintError If the length of the value is strictly
+ bigger than the limit.
+ """
+ if self.limit is not None:
+ if self.limit <= len(value):
+ return
+ raise MinSizeConstraintError("Setter : trying to set value of length %d while limit is %d"%(len(value), self.limit))
return
class ColorConstraintError(Exception):
diff --git a/src/sugar/tutorius/core.py b/src/sugar/tutorius/core.py
index f290f1e..602947f 100644
--- a/src/sugar/tutorius/core.py
+++ b/src/sugar/tutorius/core.py
@@ -295,11 +295,8 @@ class FiniteStateMachine(State):
self._states = state_dict or {}
self.start_state_name = start_state_name
- # If we have a filled input dictionary
- if len(self._states) > 0:
- self.current_state = self._states[self.start_state_name]
- else:
- self.current_state = None
+ # Set the current state to None - we are not executing anything yet
+ self.current_state = None
# Register the actions for the FSM - They will be processed at the
# FSM level, meaning that when the FSM will start, it will first
@@ -375,6 +372,13 @@ class FiniteStateMachine(State):
# state by that name - we must ignore this state change request as
# it will be done elsewhere in the hierarchy (or it's just bogus).
return
+
+ if self.current_state != None:
+ if new_state_name == self.current_state.name:
+ # If we already are in this state, we do not need to change
+ # anything in the current state - By design, a state may not point
+ # to itself
+ return
new_state = self._states[new_state_name]
diff --git a/src/sugar/tutorius/properties.py b/src/sugar/tutorius/properties.py
index 5be7e1c..a0bfa03 100644
--- a/src/sugar/tutorius/properties.py
+++ b/src/sugar/tutorius/properties.py
@@ -87,7 +87,7 @@ class TIntProperty(TutoriusProperty):
class TFloatProperty(TutoriusProperty):
"""
- Represents a floting point number. Can have an upper value limit and/or
+ Represents a floating point number. Can have an upper value limit and/or
a lower value limit.
"""
def __init__(self, value, lower_limit=None, upper_limit=None):
@@ -106,7 +106,7 @@ class TStringProperty(TutoriusProperty):
def __init__(self, value, size_limit=None):
TutoriusProperty.__init__(self)
self._type = "string"
- self.size_limit = SizeConstraint(size_limit)
+ self.size_limit = MaxSizeConstraint(size_limit)
self.set(value)
@@ -115,11 +115,11 @@ class TArrayProperty(TutoriusProperty):
Represents an array of properties. Can have a maximum number of element
limit, but there are no constraints on the content of the array.
"""
- def __init__(self, value, size_limit=None):
+ def __init__(self, value, min_size_limit=None, max_size_limit=None):
TutoriusProperty.__init__(self)
self._type = "array"
- self.size_limit = SizeConstraint(size_limit)
-
+ self.max_size_limit = MaxSizeConstraint(max_size_limit)
+ self.min_size_limit = MinSizeConstraint(min_size_limit)
self.set(value)
class TColorProperty(TutoriusProperty):
@@ -185,7 +185,7 @@ class TEnumProperty(TutoriusProperty):
class TBooleanProperty(TutoriusProperty):
"""
- Represents a True of False value.
+ Represents a True or False value.
"""
def __init__(self, value=False):
TutoriusProperty.__init__(self)
diff --git a/src/sugar/tutorius/tests/actiontests.py b/src/sugar/tutorius/tests/actiontests.py
index ab9cdba..6001ec7 100644
--- a/src/sugar/tutorius/tests/actiontests.py
+++ b/src/sugar/tutorius/tests/actiontests.py
@@ -27,24 +27,83 @@ import gtk
from sugar.tutorius.actions import *
from sugar.tutorius.services import ObjectStore
+test_props = {"prop_a":8, "prop_b":3, "prop_c":"Hi"}
+
class PropertyAction(Action):
def __init__(self, na):
- self._a = na
+ Action.__init__(self)
+ self.prop_a = TIntProperty(test_props["prop_a"])
+ self.prop_b = TIntProperty(test_props["prop_b"])
+ self.prop_c = TStringProperty(test_props["prop_c"])
+
+def has_function(obj, function_name):
+ """
+ Checks whether the object has a function by that name.
+ """
+ if hasattr(obj, function_name) and hasattr(obj.__getattribute__(function_name), "__call__"):
+ return True
+ return False
+
+class PropsTest(unittest.TestCase):
+ def test_get_properties(self):
+ act = PropertyAction(8)
+
+ assert act.get_properties() == test_props.keys(), "Action does not contain property 'a'"
+
+ for prop_name in act.get_properties():
+ assert act.properties[prop_name].value == test_props[prop_name], "Wrong initial value for property %s : %s"%(prop_name,str(act.properties[prop_name]))
+
- def set_a(self, na):
- self._a = na
+class OnceWrapperTests(unittest.TestCase):
+ def validate_wrapper(self, wrapper_instance):
+ """
+ Ensures that a given instance of a wrapper implements the Action
+ interface.
+ """
+ assert has_function(wrapper_instance, "do"),\
+ "The wrapper does not have the 'do' function"
+
+ assert has_function(wrapper_instance, "undo"),\
+ "The wrapper does not have the 'undo' function."
+
+ assert has_function(wrapper_instance, "get_properties"),\
+ "The wrapper does not have the 'get_properties' function."
+
+ def test_once_wrapper_interface(self):
+ """
+ Tests that the interface of the OnceWrapper actually conforms to the of
+ the Action.
+ """
+ prop = PropertyAction(7)
- def get_a(self):
- return self._a
+ onceWrap = OnceWrapper(prop)
+
+ self.validate_wrapper(onceWrap)
+
+ assert onceWrap.get_properties() == test_props.keys(), "OnceWrapper should give access to properties of the contained action"
- a = property(fget=get_a, fset=set_a)
+class DialogMessageTest(unittest.TestCase):
+ def setUp(self):
+ self.dial = DialogMessage("Message text", [200, 300])
-class PropsTest(unittest.TestCase):
+ def test_properties(self):
+ assert self.dial.message.value == "Message text", "Wrong start value for the message"
+
+ assert self.dial.position.value == [200, 300], "Wrong start value for the position"
- def test_get_properties(self):
- prop = PropertyAction(8)
+class BubbleMessageTest(unittest.TestCase):
+ def setUp(self):
+ self.bubble = BubbleMessage(message="Message text", pos=[200, 300], tailpos=[-15, -25])
+
+ def test_properties(self):
+ props = self.bubble.get_properties()
+
+ assert "message" in props, 'No message property of BubbleMessage'
+
+ assert "position" in props, 'No position property in BubbleMessage'
+
+ assert "tail_pos" in props, 'No tail position property in BubbleMessage'
- assert prop.get_properties() == ['a'], "Action does not contain property 'a'"
class CountAction(Action):
"""
diff --git a/src/sugar/tutorius/tests/constraintstests.py b/src/sugar/tutorius/tests/constraintstests.py
index 407cc24..b7b0a47 100644
--- a/src/sugar/tutorius/tests/constraintstests.py
+++ b/src/sugar/tutorius/tests/constraintstests.py
@@ -77,28 +77,50 @@ class LowerLimitConstraintTest(unittest.TestCase):
except LowerLimitConstraintError:
assert True, "Validation of LowerLimit(10) on 20 should not raise an exception"
-class SizeConstraintTest(unittest.TestCase):
+class MaxSizeConstraintTest(unittest.TestCase):
def test_empty_constraint(self):
- cons = SizeConstraint(None)
+ cons = MaxSizeConstraint(None)
try:
cons.validate(20)
- except SizeConstraintError:
+ except MaxSizeConstraintError:
assert False, "Empty contraint should not raise an exception"
def test_validate(self):
- cons = SizeConstraint(10)
+ cons = MaxSizeConstraint(10)
try:
cons.validate(range(0, 20))
- assert False, "Validation of SizeLimit(10) on list of length 20 should raise an exception"
- except SizeConstraintError:
+ assert False, "Validation of MaxSizeConstraint(10) on list of length 20 should raise an exception"
+ except MaxSizeConstraintError:
pass
try:
cons.validate(range(0,5))
- except SizeConstraintError:
- assert True, "Validation of SizeLimit(10) on list of length 5 should not raise an exception"
+ except MaxSizeConstraintError:
+ assert True, "Validation of MaxSizeConstraint(10) on list of length 5 should not raise an exception"
+class MinSizeConstraintTest(unittest.TestCase):
+ def test_empty_constraint(self):
+ cons = MinSizeConstraint(None)
+ try:
+ cons.validate(20)
+ except MinSizeConstraintError:
+ assert False, "Empty contraint should not raise an exception"
+
+ def test_validate(self):
+ cons = MinSizeConstraint(10)
+
+ try:
+ cons.validate(range(0, 5))
+ assert False, "Validation of MinSizeConstraint(10) on list of length 20 should raise an exception"
+ except MinSizeConstraintError:
+ pass
+
+ try:
+ cons.validate(range(0,20))
+ except MinSizeConstraintError:
+ assert True, "Validation of MinSizeConstraint(10) on list of length 5 should not raise an exception"
+
class ColorConstraintTest(unittest.TestCase):
def test_validate(self):
cons = ColorConstraint()
diff --git a/src/sugar/tutorius/tests/coretests.py b/src/sugar/tutorius/tests/coretests.py
index 5f91a64..c27846d 100644
--- a/src/sugar/tutorius/tests/coretests.py
+++ b/src/sugar/tutorius/tests/coretests.py
@@ -18,7 +18,7 @@
Core Tests
This module contains all the tests that pertain to the usage of the Tutorius
-Core. This means that the the Finite State Machine, States and all the
+Core. This means that the Event Filters, the Finite State Machine and all the
related elements and interfaces are tested here.
Usage of actions and event filters is tested, but not the concrete actions
@@ -29,7 +29,7 @@ and event filters. Those are in their separate test module
import unittest
import logging
-from sugar.tutorius.actions import Action, ClickAction, TypeTextAction
+from sugar.tutorius.actions import Action, OnceWrapper, ClickAction, TypeTextAction
from sugar.tutorius.core import *
from sugar.tutorius.filters import *
@@ -49,6 +49,14 @@ class SimpleTutorial(Tutorial):
def set_state(self, name):
self.current_state_name = name
+class TutorialWithFSM(Tutorial):
+ """
+ Fake tutorial, but associated with a FSM.
+ """
+ def __init__(self, start_name="INIT", fsm=None):
+ Tutorial.__init__(self, start_name, fsm)
+ self.activity = activity.Activity()
+
class TrueWhileActiveAction(Action):
"""
This action's active member is set to True after a do and to False after
@@ -483,7 +491,6 @@ class FSMTest(unittest.TestCase):
except Exception:
assert False, "Removing a non-existing state dit not throw the right kind of exception"
-
# Now try removing the second state
fsm.remove_state("second")
@@ -502,6 +509,33 @@ class FSMTest(unittest.TestCase):
assert "second" not in fsm.get_following_states("third"),\
"The link to second from third still exists after removal"
+ def test_set_same_state(self):
+ fsm = FiniteStateMachine("Set same state")
+
+ st1 = State("INIT")
+ st1.add_action(CountAction())
+
+ fsm.add_state(st1)
+
+ tut = SimpleTutorial()
+
+ fsm.set_tutorial(tut)
+
+ fsm.set_state("INIT")
+
+ assert fsm.get_state_by_name("INIT").get_action_list()[0].do_count == 1, \
+ "The action was not triggered on 'INIT'"
+
+ fsm.set_state("INIT")
+
+ do_count = fsm.get_state_by_name("INIT").get_action_list()[0].do_count
+ assert fsm.get_state_by_name("INIT").get_action_list()[0].do_count == 1, \
+ "The action was triggered a second time, do_count = %d"%do_count
+
+ undo_count = fsm.get_state_by_name("INIT").get_action_list()[0].undo_count
+ assert fsm.get_state_by_name("INIT").get_action_list()[0].undo_count == 0,\
+ "The action has been undone unappropriately, undo_count = %d"%undo_count
+
class FSMExplorationTests(unittest.TestCase):
def setUp(self):
self.buildFSM()
@@ -556,6 +590,7 @@ class FSMExplorationTests(unittest.TestCase):
self.validate_previous_states("Third", ("INIT", "Second"))
self.validate_previous_states("Fourth", ("Second"))
+
if __name__ == "__main__":
unittest.main()
diff --git a/src/sugar/tutorius/tests/propertiestests.py b/src/sugar/tutorius/tests/propertiestests.py
index 52a9a75..45ba264 100644
--- a/src/sugar/tutorius/tests/propertiestests.py
+++ b/src/sugar/tutorius/tests/propertiestests.py
@@ -204,7 +204,7 @@ class TStringPropertyTest(unittest.TestCase):
try:
prop.set("My string is too big!")
assert False, "String should not set to longer than max size"
- except SizeConstraintError:
+ except MaxSizeConstraintError:
pass
except:
assert False, "Wrong exception type thrown"
@@ -218,7 +218,7 @@ class TStringPropertyTest(unittest.TestCase):
try:
prop = TStringProperty("This is normal", 5)
assert False, "Creation of the property should fail."
- except SizeConstraintError:
+ except MaxSizeConstraintError:
pass
except:
assert False, "Wrong exception type on failed constructor"
@@ -236,26 +236,34 @@ class TArrayPropertyTest(unittest.TestCase):
try_wrong_values(prop)
- def test_size_limit(self):
- prop = TArrayProperty([1,2], 4)
+ def test_size_limits(self):
+ prop = TArrayProperty([1,2], None, 4)
try:
prop.set([1,2,4,5,6,7])
- assert False, "Size limit constraint was not properly applied"
- except SizeConstraintError:
+ assert False, "Maximum size limit constraint was not properly applied"
+ except MaxSizeConstraintError:
+ pass
+
+ prop = TArrayProperty([1,2,3,4], 2)
+
+ try:
+ prop.set([1])
+ assert False, "Minimum size limit constraint was not properly applied"
+ except MinSizeConstraintError:
pass
- except:
- assert False, "Wrong type of exception thrown"
-
def test_failing_constructor(self):
try:
- prop = TArrayProperty([100, 0, 20], 2)
+ prop = TArrayProperty([100, 0, 20], None, 2)
assert False, "Creation of the property should fail."
- except SizeConstraintError:
+ except MaxSizeConstraintError:
+ pass
+ try:
+ prop = TArrayProperty([100, 0, 20], 4, None)
+ assert False, "Creation of the property should fail."
+ except MinSizeConstraintError:
pass
- except:
- assert False, "Wrong exception type on failed constructor"
class TColorPropertyTest(unittest.TestCase):
def test_basic_color(self):
@@ -286,6 +294,8 @@ class TBooleanPropertyTest(unittest.TestCase):
def test_basic_boolean(self):
assert self.prop.value == False, "Could not set initial value via constructor"
+ assert self.prop.type == "boolean", "Wrong type for TBooleanProperty : %s"%self.prop.type
+
self.prop.set(True)
assert self.prop.value == True, "Could not change the value via set"
@@ -313,6 +323,8 @@ class TEnumPropertyTest(unittest.TestCase):
def test_basic_enum(self):
assert self.prop.value == "hello", "Could not set initial value on property"
+ assert self.prop.type == "enum", "Wrong type for TEnumProperty : %s"%self.prop.type
+
self.prop.set(True)
assert self.prop.value, "Could not change the value via set"
@@ -333,6 +345,8 @@ class TFilePropertyTest(unittest.TestCase):
def test_basic_file(self):
assert self.prop.value == "propertiestests.py", "Could not set initial value"
+ assert self.prop.type == "file", "Wrong type for TFileProperty : %s"%self.prop.type
+
self.prop.set("run-tests.py")
assert self.prop.value == "run-tests.py", "Could not change value"
diff --git a/src/sugar/tutorius/tests/run-tests.py b/src/sugar/tutorius/tests/run-tests.py
index 97665f7..042b10e 100755
--- a/src/sugar/tutorius/tests/run-tests.py
+++ b/src/sugar/tutorius/tests/run-tests.py
@@ -14,7 +14,6 @@ SUBDIRS = ["uam"]
GLOB_PATH = os.path.join(FULL_PATH,"*.py")
import unittest
from glob import glob
-
def report_files():
ret = glob(GLOB_PATH)
for dir in SUBDIRS:
diff --git a/src/sugar/tutorius/tests/serializertests.py b/src/sugar/tutorius/tests/serializertests.py
index 75fe8eb..2a743e1 100644
--- a/src/sugar/tutorius/tests/serializertests.py
+++ b/src/sugar/tutorius/tests/serializertests.py
@@ -39,6 +39,28 @@ from sugar.tutorius.bundler import *
from uuid import *
import rpdb2
+class SerializerInterfaceTest(unittest.TestCase):
+ """
+ For completeness' sake.
+ """
+ def test_save(self):
+ ser = Serializer()
+
+ try:
+ ser.save_fsm(None)
+ assert False, "save_fsm() should throw an unimplemented error"
+ except:
+ pass
+
+ def test_load(self):
+ ser = Serializer()
+
+ try:
+ ser.load_fsm(str(uuid.uuid1))
+ assert False, "load_fsm() should throw an unimplemented error"
+ except:
+ pass
+
class XMLSerializerTest(unittest.TestCase):
"""
Tests the transformation of XML to FSM, then back.
@@ -104,10 +126,11 @@ class XMLSerializerTest(unittest.TestCase):
'FSM underlying dictionary differ from original to pickled/reformed one'
assert loaded_fsm._states.get("Second").name == self.fsm._states.get("Second").name, \
'FSM underlying dictionary differ from original to pickled/reformed one'
- assert loaded_fsm._states.get("INIT").get_action_list()[0].get_message() == \
- self.fsm._states.get("INIT").get_action_list()[0].get_message(), \
+ assert loaded_fsm._states.get("INIT").get_action_list()[0].message.value == \
+ self.fsm._states.get("INIT").get_action_list()[0].message.value, \
'FSM underlying State underlying Action differ from original to reformed one'
-
+ assert len(loaded_fsm.get_action_list()) == 0, "FSM should not have any actions on itself"
+
os.remove(os.path.join(sugar.tutorius.bundler._get_store_root(), str(self.uuid)) + "/fsm.xml")
if os.path.isdir(testpath):
shutil.rmtree(testpath)