Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/sugar/activity/activity.py14
-rw-r--r--src/sugar/tutorius/Makefile.am6
-rw-r--r--src/sugar/tutorius/actions.py63
-rw-r--r--src/sugar/tutorius/bundler.py247
-rwxr-xr-xsrc/sugar/tutorius/tests/run-tests.py3
-rw-r--r--src/sugar/tutorius/tests/serializertests.py105
6 files changed, 328 insertions, 110 deletions
diff --git a/src/sugar/activity/activity.py b/src/sugar/activity/activity.py
index a5188fd..3e2d3d4 100644
--- a/src/sugar/activity/activity.py
+++ b/src/sugar/activity/activity.py
@@ -77,6 +77,7 @@ from sugar.datastore import datastore
from sugar.session import XSMPClient
from sugar import wm
from sugar.tutorius.services import ObjectStore
+from sugar.tutorius.tutoserialize import TutoSerializer
_ = lambda msg: gettext.dgettext('sugar-toolkit', msg)
@@ -128,15 +129,14 @@ class ActivityToolbar(gtk.Toolbar):
self.tutorials.combo.connect('changed', self.__tutorial_changed_cb)
# Get tutorial list by file
logging.debug("************************************ before creating serialize")
-## serialize = TutoSerializer()
-## logging.debug("************************************ before calling load_tuto_list()")
-##
-## #tutorials = self._activity.get_tutorials()
-## if getattr(self._activity,"_tutorials",None) is None:
-## tutorials = serialize.load_tuto_list()
+ serialize = TutoSerializer()
+ logging.debug("************************************ before calling load_tuto_list()")
+
+ #tutorials = self._activity.get_tutorials()
+ if getattr(self._activity,"_tutorials",None) is None:
+ tutorials = serialize.load_tuto_list()
self._current_tutorial = None
- tutorials = None
if tutorials:
for key, tutorial in tutorials.items():
# self.tutorials.combo.append_item(key, _(tutorial.name))
diff --git a/src/sugar/tutorius/Makefile.am b/src/sugar/tutorius/Makefile.am
index 8805314..7223c60 100644
--- a/src/sugar/tutorius/Makefile.am
+++ b/src/sugar/tutorius/Makefile.am
@@ -1,3 +1,5 @@
+SUBDIRS = uam
+
sugardir = $(pythondir)/sugar/tutorius
sugar_PYTHON = \
__init__.py \
@@ -9,5 +11,7 @@ sugar_PYTHON = \
services.py \
overlayer.py \
editor.py \
- linear_creator.py\
+ linear_creator.py \
+ constraints.py \
+ properties.py \
bundler.py
diff --git a/src/sugar/tutorius/actions.py b/src/sugar/tutorius/actions.py
index d81e3c2..d2516f7 100644
--- a/src/sugar/tutorius/actions.py
+++ b/src/sugar/tutorius/actions.py
@@ -29,7 +29,7 @@ class Action(object):
"""Base class for Actions"""
def __init__(self):
object.__init__(self)
- self.properties = {}
+ self.properties = None
def do(self, **kwargs):
"""
@@ -44,7 +44,12 @@ class Action(object):
pass #Should raise NotImplemented?
def get_properties(self):
- if self.properties is None or len(self.properties) == 0:
+ """
+ Fills self.property with a dict of TutoriusProperty and return the list
+ of property names. get_properties has to be called before accessing
+ self.property
+ """
+ if self.properties is None:
self.properties = {}
for i in dir(self):
if isinstance(getattr(self,i), TutoriusProperty):
@@ -130,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
@@ -217,53 +222,54 @@ class DisableWidgetAction(Action):
self._widget = gtkutils.find_widget(os.activity, self._target)
if self._widget:
self._widget.set_sensitive(False)
-
- def undo(self):
+
+ def undo(self):
"""Action undo"""
if self._widget:
self._widget.set_sensitive(True)
-
+
+
class TypeTextAction(Action):
- """
+ """
Simulate a user typing text in a widget
Work on any widget that implements a insert_text method
-
+
@param widget The treehish representation of the widget
@param text the text that is typed
- """
+ """
def __init__(self, widget, text):
Action.__init__(self)
-
+
self._widget = widget
self._text = text
-
+
def do(self, **kwargs):
- """
- Type the text
- """
+ """
+ Type the text
+ """
widget = gtkutils.find_widget(ObjectStore().activity, self._widget)
if hasattr(widget, "insert_text"):
widget.insert_text(self._text, -1)
-
- def undo(self):
- """
- no undo
- """
- pass
-
+
+ def undo(self):
+ """
+ no undo
+ """
+ pass
+
class ClickAction(Action):
- """
+ """
Action that simulate a click on a widget
Work on any widget that implements a clicked() method
-
+
@param widget The threehish representation of the widget
- """
+ """
def __init__(self, widget):
Action.__init__(self)
self._widget = widget
-
- def do(self):
- """
+
+ def do(self):
+ """
click the widget
"""
widget = gtkutils.find_widget(ObjectStore().activity, self._widget)
@@ -275,3 +281,4 @@ class ClickAction(Action):
No undo
"""
pass
+
diff --git a/src/sugar/tutorius/bundler.py b/src/sugar/tutorius/bundler.py
index a28d6ef..34b3a12 100644
--- a/src/sugar/tutorius/bundler.py
+++ b/src/sugar/tutorius/bundler.py
@@ -24,17 +24,17 @@ import logging
import os
import uuid
import xml.dom.minidom
-import xml.dom.ext
from sugar.tutorius import gtkutils, overlayer
from sugar.tutorius.core import Tutorial, State, FiniteStateMachine
-import sugar.tutorius.actions
+from sugar.tutorius.actions import *
import sugar.tutorius.filters
+#from sugar.tutorius.filters import EventFilter, TimerEvent, GtkWidgetEventFilter, GtkWidgetTypeFilter
from ConfigParser import SafeConfigParser
-def __get_store_root():
+def _get_store_root():
return os.path.join(os.getenv("SUGAR_PREFIX"),"share","tutorius","data")
-def __get_bundle_root():
+def _get_bundle_root():
return os.path.join(os.getenv("SUGAR_BUNDLE_PATH"),"data","tutorius","data")
INI_ACTIVITY_SECTION = "RELATED_ACTIVITIES"
@@ -51,8 +51,8 @@ class TutorialStore:
given activity.
"""
- store_root = __get_store_root()
- bundle_root = __get_bundle_root()
+ store_root = _get_store_root()
+ bundle_root = _get_bundle_root()
logging.debug("*********** Path of store_root : " + store_root)
@@ -119,70 +119,90 @@ class XMLSerializer(Serializer):
used in the tutorials to/from a .xml file. Inherit from Serializer
"""
- def create_state_dict_node(self, state_dict, doc):
+ def _create_state_dict_node(self, state_dict, doc):
"""
Create and return a xml Node from a State dictionnary.
"""
statesList = doc.createElement("States")
for state_name, state in state_dict.items():
- stateNode = statesList.appendChild("State")
- stateNode.setAttribute("State:Name", state_name)
- stateNode = stateNode.appendChild(create_action_list_node(state.action_list, doc))
- stateNode = stateNode.appendChild(create_event_filters_node(state.event_filters, doc))
+ stateNode = doc.createElement("State")
+ statesList.appendChild(stateNode)
+ stateNode.setAttribute("Name", state_name)
+ actionsList = stateNode.appendChild(self._create_action_list_node(state.get_action_list(), doc))
+ eventfiltersList = stateNode.appendChild(self._create_event_filters_node(state.get_event_filter_list(), doc))
return statesList
- def create_action_list_node(self, action_list, doc):
+ def _create_action_list_node(self, action_list, doc):
"""
Create and return a xml Node from a Action list.
"""
actionsList = doc.createElement("Actions")
for action in action_list:
- actionNode = actionsList.appendChild("Action")
+ 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:
- actionNode.setAttribute("Action:Class", type(action))
- actionNode.setAttribute("Action:Message", action.message)
- actionNode.setAttribute("Action: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("Action:Class", str(type(action)))
- actionNode.setAttribute("Action:Message", action.message)
- actionNode.setAttribute("Action:Position", action.position)
- actionNode.setAttribute("Action:Tail_pos", 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("Action:Class", str(type(action)))
- # TODO
+ # Nothing else to save
+ pass
elif type(action) is ChainAction:
- # TODO
- actionNode.setAttribute("Action:Class", str(type(action)))
+ # 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("Action:Class", str(type(action)))
+ # Remember the target
+ actionNode.setAttribute("Target", action._target)
elif type(action) is TypeTextAction:
- # TODO
- actionNode.setAttribute("Action:Class", str(type(action)))
+ # Save the text and the widget
+ actionNode.setAttribute("Widget", action._widget)
+ actionNode.setAttribute("Text", action._text)
elif type(action) is ClickAction:
- # TODO
- actionNode.setAttribute("Action:Class", str(type(action)))
+ # Save the widget to click
+ actionNode.setAttribute("Widget", action._widget)
return actionsList
- def create_event_filters_node(self, event_filters, doc):
+ def _create_event_filters_node(self, event_filters, doc):
"""
Create and return a xml Node from a event filters.
"""
eventFiltersList = doc.createElement("EventFiltersList")
for event_f in event_filters:
eventFilterNode = eventFiltersList.appendChild("EventFilter")
- # TODO : elif for each type of event filters
- if type(event_f) is TimerEvent:
- # TODO
- eventFilterNode.setAttribute("EventFilter:Class", str(type(event_f)))
- elif type(event_f) is GtkWidgetEventFilter:
- # TODO
- eventFilterNode.setAttribute("EventFilter:Class", str(type(event_f)))
- elif type(event_f) is GtkWidgetTypeFilter:
- # TODO
- eventFilterNode.setAttribute("EventFilter:Class", str(type(event_f)))
+
+ # 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:
+ eventFilterNode.setAttribute("Timeout_s", str(event_f._timeout))
+
+ elif type(event_f) is sugar.tutorius.filters.GtkWidgetEventFilter:
+ eventFilterNode.setAttribute("EventName", event_f._event_name)
+ eventFilterNode.setAttribute("ObjectId", event_f._object_id)
+
+ elif type(event_f) is sugar.tutorius.filters.GtkWidgetTypeFilter:
+ 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
@@ -194,15 +214,18 @@ class XMLSerializer(Serializer):
doc = xml.dom.minidom.Document()
fsm_element = doc.createElement("FSM")
doc.appendChild(fsm_element)
- fsm_element.setAttribute("fsm:Name", fsm.name)
- fsm_element.setAttribute("fsm:StartStateName", fsm.start_state_name)
- fsm_element = fsm_element.appendChild(create_state_dict_node(fsm.state_dict, doc))
- fsm_element = fsm_element.appendChild(create_action_list_node(fsm.action_list, doc))
+ 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))
+
+ 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(path + "/" + xml_filename, "w")
- xml.dom.ext.PrettyPrint(doc, file_object)
+ file_object = open(os.path.join(path, xml_filename), "w")
+ file_object.write(doc.toprettyxml())
file_object.close()
-
+
def _find_tutorial_dir_with_guid(self, guid):
"""
@@ -218,12 +241,12 @@ class XMLSerializer(Serializer):
@param guid The GUID of the tutorial that is to be loaded.
"""
# Attempt to find the tutorial's directory in the global directory
- global_dir = os.path.join(__get_store_root(), guid)
+ global_dir = os.path.join(_get_store_root(), guid)
# Then in the activty's bundle path
- activity_dir = os.path.join(__get_bundle_root(), guid)
+ activity_dir = os.path.join(_get_bundle_root(), guid)
# If they both exist
- if os.path.isdir(global_filename) and os.path.isdir(activity_dir):
+ if os.path.isdir(global_dir) and os.path.isdir(activity_dir):
# Inspect both metadata files
global_meta = os.path.join(global_dir, "meta.ini")
activity_meta = os.path.join(activity_dir, "meta.ini")
@@ -254,7 +277,7 @@ class XMLSerializer(Serializer):
return activity_dir
# Error : none of these directories contain the tutorial
- raise IOError(2, "Neither the global nor the bundle directory contained the tutorial with GUID"%guid)
+ raise IOError(2, "Neither the global nor the bundle directory contained the tutorial with GUID %s"%guid)
def _load_xml_properties(self, properties_elem):
"""
@@ -271,7 +294,40 @@ class XMLSerializer(Serializer):
@param filters_elem An XML Element representing a list of event filters
"""
- return []
+ reformed_event_filters_list = []
+ # 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:
+ # 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):
+ timeout = int(event_filter.getAttribute("Timeout_s"))
+ new_event_filter = TimerEvent(next_state, timeout)
+
+ elif event_filter.getAttribute("Class") == str(sugar.tutorius.filters.GtkWidgetEventFilter):
+ # 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):
+ # 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
def _load_xml_actions(self, actions_elem):
"""
@@ -279,7 +335,51 @@ class XMLSerializer(Serializer):
@param actions_elem An XML Element representing a list of Actions
"""
- return []
+ reformed_actions_list = []
+ # item(0) because there is always only one <Actions> tag in the xml file
+ # so states_elem should always contain only one element
+ actions_element_list = actions_elem.item(0).getElementsByTagName("Action")
+
+ for action in actions_element_list:
+ # TODO : elif for each type of action
+ if action.getAttribute("Class") == str(DialogMessage):
+ message = action.getAttribute("Message")
+ 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")
+ 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):
+ reformed_actions_list.append(WidgetIdentifyAction())
+ elif action.getAttribute("Class") == str(ChainAction):
+ # Load the subactions
+ subActionsList = _load_xml_actions(action.getElementsByTagName("Actions"))
+ reformed_actions_list.append(ChainAction(subActionsList))
+ elif action.getAttribute("Class") == str(DisableWidgetAction):
+ # Get the target
+ targetName = action.getAttribute("Target")
+ reformed_actions_list.append(DisableWidgetAction(targetName))
+ elif action.getAttribute("Class") == str(TypeTextAction):
+ # 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):
+ # Load the widget to click
+ widget = action.getAttribute("Widget")
+
+ reformed_actions_list.append(ClickAction(widget))
+
+ return reformed_actions_list
def _load_xml_states(self, states_elem):
"""
@@ -288,7 +388,18 @@ class XMLSerializer(Serializer):
@param states_elem An XML Element that represents a list of States
"""
- return []
+ reformed_state_list = []
+ # item(0) because there is always only one <States> tag in the xml file
+ # so states_elem should always contain only one element
+ states_element_list = states_elem.item(0).getElementsByTagName("State")
+
+ for state in states_element_list:
+ stateName = state.getAttribute("Name")
+ actions_list = self._load_xml_actions(state.getElementsByTagName("Actions"))
+ event_filters_list = self._load_xml_event_filters(state.getElementsByTagName("EventFiltersList"))
+ reformed_state_list.append(State(stateName, actions_list, event_filters_list))
+
+ return reformed_state_list
def _load_xml_fsm(self, fsm_elem):
"""
@@ -298,28 +409,28 @@ class XMLSerializer(Serializer):
@param fsm_elem The XML element that describes a FSM
"""
# Load the FSM's name and start state's name
- fsm_name = fsm_elem.getAttribute("fsm:Name")
+ fsm_name = fsm_elem.getAttribute("Name")
fsm_start_state_name = None
try:
- fsm_start_state_name = fsm_elem.getAttribute("fsm:StartStateName")
+ fsm_start_state_name = fsm_elem.getAttribute("StartStateName")
except:
pass
fsm = FiniteStateMachine(fsm_name, start_state_name=fsm_start_state_name)
# Load the states
- states = self._load_xml_states(fsm_elem.getElementsByName("States"))
+ states = self._load_xml_states(fsm_elem.getElementsByTagName("States"))
for state in states:
fsm.add_state(state)
# Load the actions on this FSM
- actions = self._load_xml_actions(fsm_elem.getElementsByName("Actions"))
+ actions = self._load_xml_actions(fsm_elem.getElementsByTagName("FSMActions"))
for action in actions:
fsm.add_action(action)
# Load the event filters
- events = self._load_xml_event_filters(fsm_elem.getElementsByName("EventFiltersList"))
+ events = self._load_xml_event_filters(fsm_elem.getElementsByTagName("EventFiltersList"))
for event in events:
fsm.add_event_filter(event)
@@ -338,7 +449,7 @@ class XMLSerializer(Serializer):
xml_dom = xml.dom.minidom.parse(tutorial_file)
- fsm_elem = xml_dom.getElementsByTagName("fsm")[0]
+ fsm_elem = xml_dom.getElementsByTagName("FSM")[0]
return self._load_xml_fsm(fsm_elem)
@@ -351,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,
@@ -362,12 +472,12 @@ class TutorialBundler:
#Look for the file in the path if a uid is supplied
if generated_guid:
#General store
- store_path = os.path.join(__get_store_root(), generated_guid, INI_FILENAME)
+ store_path = os.path.join(_get_store_root(), generated_guid, INI_FILENAME)
if os.path.isfile(store_path):
self.Path = os.path.dirname(store_path)
else:
#Bundle store
- bundle_path = os.path.join(__get_bundle_root(), generated_guid, INI_FILENAME)
+ bundle_path = os.path.join(_get_bundle_root(), generated_guid, INI_FILENAME)
if os.path.isfile(bundle_path):
self.Path = os.path.dirname(bundle_path)
else:
@@ -375,7 +485,7 @@ class TutorialBundler:
else:
#Create the folder, any failure will go through to the caller for now
- store_path = os.path.join(__get_store_root(), generated_guid)
+ store_path = os.path.join(_get_store_root(), generated_guid)
os.mkdir(store_path)
self.Path = store_path
@@ -424,8 +534,8 @@ class TutorialBundler:
more than one path, the store_root is given priority.
"""
- store_root = __get_store_root()
- bundle_root = __get_bundle_root()
+ store_root = _get_store_root()
+ bundle_root = _get_bundle_root()
config = SafeConfigParser()
path = None
@@ -483,10 +593,11 @@ class TutorialBundler:
config = SafeConfigParser()
if guid is not None:
+ serializer = XMLSerializer()
path = get_tutorial_path() + "/meta.ini"
config.read(path)
xml_filename = config.get(INI_METADATA_SECTION, INI_XML_FSM_PROPERTY)
- save_fsm(fsm, xml_filename, store_root)
+ serializer.save_fsm(fsm, xml_filename, store_root)
def add_resources(self, typename, file):
diff --git a/src/sugar/tutorius/tests/run-tests.py b/src/sugar/tutorius/tests/run-tests.py
index 1fc534e..042b10e 100755
--- a/src/sugar/tutorius/tests/run-tests.py
+++ b/src/sugar/tutorius/tests/run-tests.py
@@ -51,8 +51,7 @@ if __name__=='__main__':
suite.addTests(unittest.findTestCases(filterstests))
suite.addTests(unittest.findTestCases(constraintstests))
suite.addTests(unittest.findTestCases(propertiestests))
- suite.addTests(unittest.findTestCases(serializertests))
-
+ suite.addTests(unittest.findTestCases(serializertests))
runner = unittest.TextTestRunner()
runner.run(suite)
coverage.stop()
diff --git a/src/sugar/tutorius/tests/serializertests.py b/src/sugar/tutorius/tests/serializertests.py
index a53e196..2a743e1 100644
--- a/src/sugar/tutorius/tests/serializertests.py
+++ b/src/sugar/tutorius/tests/serializertests.py
@@ -28,21 +28,118 @@ import unittest
import logging
import linecache
import os
-import cPickle as pickle
+import shutil
from sugar.tutorius import gtkutils, overlayer
from sugar.tutorius.core import Tutorial, State, FiniteStateMachine
from sugar.tutorius.actions import DialogMessage, OnceWrapper, BubbleMessage
from sugar.tutorius.filters import GtkWidgetEventFilter, TimerEvent
+from sugar.tutorius.bundler import *
+from uuid import *
+import rpdb2
-# Helper classes to help testing
-
+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.
+ """
+ def setUp(self):
+ # Create the sample FSM
+ self.fsm = FiniteStateMachine("testingMachine")
+
+ # Add a few states
+ act1 = BubbleMessage(message="Hi", pos=[300, 450])
+ ev1 = GtkWidgetEventFilter("0.12.31.2.2", "clicked", "Second")
+ act2 = BubbleMessage(message="Second message", pos=[250, 150], tailpos=[1,2])
+
+ st1 = State("INIT")
+ st1.add_action(act1)
+
+ st2 = State("Second")
+
+ st2.add_action(act2)
+
+ self.fsm.add_state(st1)
+ self.fsm.add_state(st2)
+
+ self.uuid = uuid1()
+
+
+ def test_save(self, remove=True):
+ """
+ Writes an FSM to disk, then compares the file to the expected results.
+ "Remove" boolean argument specify if the test data must be removed or not
+ """
+ # Make the serializer believe the test is in a activity path
+ testpath = "/tmp/testdata/"
+ os.environ["SUGAR_BUNDLE_PATH"] = testpath
+ os.environ["SUGAR_PREFIX"] = testpath
+## os.mkdir(sugar.tutorius.bundler._get_store_root())
+ xml_ser = XMLSerializer()
+ os.makedirs(os.path.join(sugar.tutorius.bundler._get_store_root(), str(self.uuid)))
+ #rpdb2.start_embedded_debugger('flakyPass')
+ xml_ser.save_fsm(self.fsm, "fsm.xml", os.path.join(sugar.tutorius.bundler._get_store_root(), str(self.uuid)))
+
+ #Remove test file and path
+ if remove == True:
+ os.remove(os.path.join(sugar.tutorius.bundler._get_store_root(), str(self.uuid)) + "/fsm.xml")
+ if os.path.isdir(testpath):
+ shutil.rmtree(testpath)
+
+ def test_save_and_load(self):
+ """
+ Load up the written FSM and compare it with the object representation.
+ """
+ self.test_save(False)
+ testpath = "/tmp/testdata/"
+ #rpdb2.start_embedded_debugger('flakyPass')
+ xml_ser = XMLSerializer()
+
+ # This interface needs to be redone... It's not clean because there is
+ # a responsibility mixup between the XML reader and the bundler.
+ loaded_fsm = xml_ser.load_fsm(str(self.uuid))
+
+ # Compare the two FSMs
+ assert loaded_fsm._states.get("INIT").name == self.fsm._states.get("INIT").name, \
+ '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].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)
+# Helper classes to help testing
class SerializerTest(unittest.TestCase):
"""
+
This class has to test the Serializer methods as well as the expected
functionality.
"""
@@ -119,4 +216,4 @@ class SerializerTest(unittest.TestCase):
if __name__ == "__main__":
- unittest.main() \ No newline at end of file
+ unittest.main()