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-10-12 06:54:12 (GMT)
committer mike <michael.jmontcalm@gmail.com>2009-10-12 06:54:12 (GMT)
commit74dcbcb643cfd66df071020320ba6c0004e92c17 (patch)
treefa5cddc37bb337f90514601cf63a79c51610ad72
parente113f3ca55178e248f620948936d8c86d22e26c2 (diff)
LP 448319 : Refactoring to translate function, adding tests for translation of resourceslp448319
-rwxr-xr-xtests/run-tests.py3
-rw-r--r--tests/translatortests.py91
-rw-r--r--tutorius/translator.py119
3 files changed, 177 insertions, 36 deletions
diff --git a/tests/run-tests.py b/tests/run-tests.py
index 23d7e24..9b4fa25 100755
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -41,6 +41,7 @@ if __name__=='__main__':
import propertiestests
import serializertests
import probetests
+ import translatortests
suite = unittest.TestSuite()
suite.addTests(unittest.findTestCases(coretests))
suite.addTests(unittest.findTestCases(servicestests))
@@ -54,6 +55,7 @@ if __name__=='__main__':
suite.addTests(unittest.findTestCases(propertiestests))
suite.addTests(unittest.findTestCases(serializertests))
suite.addTests(unittest.findTestCases(probetests))
+ suite.addTests(unittest.findTestCases(translatortests))
runner = unittest.TextTestRunner()
runner.run(suite)
coverage.stop()
@@ -73,5 +75,6 @@ if __name__=='__main__':
from actiontests import *
from serializertests import *
from probetests import *
+ from translatortests import *
unittest.main()
diff --git a/tests/translatortests.py b/tests/translatortests.py
new file mode 100644
index 0000000..0f053b9
--- /dev/null
+++ b/tests/translatortests.py
@@ -0,0 +1,91 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import unittest
+import os
+
+from sugar.tutorius.translator import *
+from sugar.tutorius.properties import *
+
+##############################################################################
+## Helper classes
+class ResourceAction(TPropContainer):
+ resource = TResourceProperty()
+
+ def __init__(self):
+ TPropContainer.__init__(self)
+
+class NestedResource(TPropContainer):
+ nested = TAddonProperty()
+
+ def __init__(self):
+ TPropContainer.__init__(self)
+ self.nested = ResourceAction()
+
+class ListResources(TPropContainer):
+ nested_list = TAddonListProperty()
+
+ def __init__(self):
+ TPropContainer.__init__(self)
+ self.nested_list = [ResourceAction(), ResourceAction()]
+
+##
+##############################################################################
+
+class ResourceTranslatorTests(unittest.TestCase):
+ temp_path = "/tmp/tutorius"
+ file_name = "temp.txt"
+
+ def setUp(self):
+ try:
+ os.mkdir(self.temp_path)
+ except:
+ pass
+ new_file = file(os.path.join(self.temp_path, self.file_name), "w")
+
+ # Use a dummy prob manager - we shouldn't be using it
+ self.prob_man = object()
+
+ self.translator = ResourceTranslator(self.prob_man, '0101010101', testing=True)
+
+ pass
+
+ def tearDown(self):
+ os.unlink(os.path.join(self.temp_path, self.file_name))
+ pass
+
+ def test_translate(self):
+ # Create an action with a resource property
+ res_action = ResourceAction()
+
+ self.translator.translate(res_action)
+
+ assert getattr(res_action, "resource").type == "file", "Resource was not converted to file"
+
+ def test_recursive_translate(self):
+ nested_action = NestedResource()
+
+ self.translator.translate(nested_action)
+
+ assert getattr(getattr(nested_action, "nested"), "resource").type == "file", "Nested resource was not converted properly"
+
+ def test_list_translate(self):
+ list_action = ListResources()
+
+ self.translator.translate(list_action)
+
+ for container in list_action.nested_list:
+ assert getattr(container, "resource").type == "file", "Element of list was not converted properly"
diff --git a/tutorius/translator.py b/tutorius/translator.py
index 884714e..9925346 100644
--- a/tutorius/translator.py
+++ b/tutorius/translator.py
@@ -20,9 +20,9 @@ import copy
logger = logging.getLogger("ResourceTranslator")
-
from sugar.tutorius.properties import *
-from sugar.tutorius.vault import *
+# TODO : Uncomment this line upon integration with the Vault
+##from sugar.tutorius.vault import *
class ResourceTranslator(object):
"""
@@ -30,9 +30,15 @@ class ResourceTranslator(object):
properties before action execution. This class works as a decorator
to the ProbeManager class, as it is meant to be a transparent layer
before sending the action to execution.
+
+ An architectural note : every different type of translation should have its
+ own method (translate_resource, etc...), and this function must be called
+ from the translate method, under the type test. The translation_* method
+ must take in the input property and give the output property that should
+ replace it.
"""
- def __init__(self, probe_manager, tutorial_id):
+ def __init__(self, probe_manager, tutorial_id, testing=False):
"""
Creates a new ResourceTranslator for the given tutorial. This
translator is tasked with replacing resource properties of the
@@ -43,11 +49,15 @@ class ResourceTranslator(object):
@param probe_manager The probe manager to decorate
@param tutorial_id The ID of the current tutorial
+
+ @param testing Triggers the usage of a fake vault for testing purposes
"""
self._probe_manager = probe_manager
self._tutorial_id = tutorial_id
- def _replace_resources(self, prop_container):
+ self._testing = testing
+
+ def translate_resource(self, res_prop):
"""
Replace the TResourceProperty in the container by their
runtime-defined file equivalent. Since the resources are stored
@@ -56,34 +66,81 @@ class ResourceTranslator(object):
to transform the resource identifier into the absolute path for
the process to be able to use it properly.
+ @param res_prop The resource property to be translated
+ @return The TFileProperty corresponding to this resource, containing
+ an absolute path to the resource
+ """
+ # We need to replace the resource by a file representation
+ filepath = ""
+ # TODO : Refactor when the Vault will be available
+ if not self._testing:
+ filepath = Vault.get_resource_path(self._tutorial_id, \
+ prop_object.value)
+ else:
+ filepath = "/tmp/tutorius/temp.txt"
+ # Create the new file representation
+ file_prop = TFileProperty(filepath)
+
+ return file_prop
+
+ def translate(self, prop_container):
+ """
+ Applies the required translation to be able to send the container to an
+ executing endpoint (like a Probe). For each type of property that
+ requires it, there is translation function that will take care of
+ mapping the property to its executable form.
+
This function does not return anything, but its post-condition is
- that all the resources properties of container have been replaced
- by its corresponding file property on disk.
+ that all the properties of the input container have been replaced
+ by their corresponding executable versions.
+
+ An example of translation is taking a resource (a relative path to
+ a file under the tutorial folder) and transforming it into a file
+ (a full absolute path) to be able to load it when the activity receives
+ the action.
@param prop_container The property container in which we want to
replace all the resources for file properties and
to recursively do so for addon and addonlist
properties.
"""
- for propname in action.get_properties():
- prop_object = getattr(action, propname)
- prop_type = prop_object.type
+ for propname in prop_container.get_properties():
+ prop_value = getattr(prop_container, propname)
+ prop_type = getattr(type(prop_container), propname).type
+ # If the property is a resource, then we need to query the
+ # vault to create its correspondent
if prop_type == "resource":
- # We need to replace the resource by a file representation
- filepath = vault.get_resource_path(self._tutorial_id, \
- prop_object.value)
- # Create the new file representation
- file_prop = TFileProperty(filepath)
- # Replace the property
- setattr(action, propname, file_prop)
- elif prob_type == "addon":
- self._replace_resources(prop_object.value)
- elif prob_type == "addonlist":
- for container in prop_object.value:
- self._replace_resources(container.value)
+ # Apply the translation
+ file_prop = self.translate_resource(prop_value)
+ # Set the property with the new value
+ setattr(prop_container, propname, file_prop)
+
+ # If the property is an addon, then its value IS a
+ # container too - we need to translate it
+ elif prop_type == "addon":
+ # Translate the sub properties
+ self.translate(prop_value)
+
+ # If the property is an addon list, then we need to translate all
+ # the elements of the list
+ elif prop_type == "addonlist":
+ # Now, for each sub-container in the list, we will apply the
+ # translation processing. This is done by popping the head of
+ # the list, translating it and inserting it back at the end.
+ for index in range(0, len(prop_value)):
+ # Pop the head of the list
+ container = prop_value[0]
+ del prop_value[0]
+ # Translate the sub-container
+ self.translate(container)
+
+ # Put the value back in the list
+ prop_value.append(container)
+ # Change the list contained in the addonlist property, since
+ # we got a copy of the list when requesting it
+ setattr(prop_container, propname, prop_value)
-
### ProbeManager interface for decorator ###
## Unchanged functions ##
@@ -112,35 +169,25 @@ class ResourceTranslator(object):
## Decorated functions ##
def install(self, action):
# Make a new copy of the action that we want to install,
- # because _replace_resources changes the action and we
+ # because translate() changes the action and we
# don't want to modify the caller's action representation
new_action = copy.deepcopy(action)
# Execute the replacement
- self._replace_resources(new_action)
+ self.translate(new_action)
# Send the new action to the probe manager
return self._probe_manager.install(new_action)
def update(self, action):
- # Make a new copy of the action that we want to install,
- # because _replace_resources changes the action and we
- # don't want to modify the caller's action representation
new_action = copy.deepcopy(action)
- # Execute the replacement
- self._replace_resources(new_action)
+ self.translate(new_action)
- # Send the new action to the probe manager
return self._probe_manager.update(new_action)
def uninstall(self, action):
- # Make a new copy of the action that we want to install,
- # because _replace_resources changes the action and we
- # don't want to modify the caller's action representation
new_action = copy.deepcopy(action)
- # Execute the replacement
- self._replace_resources(new_action)
+ self.translate(new_action)
- # Send the new action to the probe manager
return self._probe_manager.uninstall(new_action)
def uninstall_all(self):