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-06 17:24:41 (GMT)
committer mike <michael.jmontcalm@gmail.com>2009-04-06 17:24:41 (GMT)
commit29177ac24ce70208a6225669a1275f67ac0f647a (patch)
tree3dccae52fb1fb6735dd1aa7a9e8d8c65a5c24e6d
parentbbc61e78c1f47537f9a7118073ca89f06ad53ea9 (diff)
parent86baeb8e6b7e538e823574773c41536e38ea9d42 (diff)
Merge branch 'master' of ssh://mike@bobthebuilder.mine.nu:8080/home/git
-rw-r--r--src/sugar/tutorius/Makefile.am4
-rw-r--r--src/sugar/tutorius/constraints.py189
-rw-r--r--src/sugar/tutorius/properties.py204
-rw-r--r--src/sugar/tutorius/tests/constraintstests.py211
-rw-r--r--src/sugar/tutorius/tests/propertiestests.py348
-rwxr-xr-xsrc/sugar/tutorius/tests/run-tests.py5
6 files changed, 960 insertions, 1 deletions
diff --git a/src/sugar/tutorius/Makefile.am b/src/sugar/tutorius/Makefile.am
index 9ff425e..deabd25 100644
--- a/src/sugar/tutorius/Makefile.am
+++ b/src/sugar/tutorius/Makefile.am
@@ -9,4 +9,6 @@ sugar_PYTHON = \
services.py \
overlayer.py \
editor.py \
- linear_creator.py
+ linear_creator.py \
+ constraints.py \
+ properties.py
diff --git a/src/sugar/tutorius/constraints.py b/src/sugar/tutorius/constraints.py
new file mode 100644
index 0000000..a666ecb
--- /dev/null
+++ b/src/sugar/tutorius/constraints.py
@@ -0,0 +1,189 @@
+# 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
+"""
+Constraints
+
+Defines a set of constraints with their related errors. These constraints are
+made to be used inside TutoriusProperties in order to limit the values that
+they might take. They can also be used to enforce a particular format or type
+for some properties.
+"""
+
+# For the File Constraint
+import os
+
+class Constraint():
+ """
+ Basic block for defining constraints on a TutoriusProperty. Every class
+ inheriting from Constraint will have a validate function that will be
+ executed when the property's value is to be changed.
+ """
+ def validate(self, value):
+ """
+ This function receives the value that is proposed as a new value for
+ the property. It needs to raise an Error in the case where the value
+ does not respect this constraint.
+ """
+ raise NotImplementedError("Unable to validate a base Constraint")
+
+class ValueConstraint(Constraint):
+ """
+ A value constraint contains a _limit member that can be used in a children
+ class as a basic value. See UpperLimitConstraint for an exemple.
+ """
+ def __init__(self, limit):
+ self.limit = limit
+
+class UpperLimitConstraintError(Exception):
+ pass
+
+class UpperLimitConstraint(ValueConstraint):
+ def validate(self, value):
+ """
+ Evaluates whether the given value is smaller than the limit.
+
+ @raise UpperLimitConstraintError When the value is strictly higher than
+ the limit.
+ """
+ if self.limit is not None:
+ if self.limit >= value:
+ return
+ raise UpperLimitConstraintError()
+ return
+
+class LowerLimitConstraintError(Exception):
+ pass
+
+class LowerLimitConstraint(ValueConstraint):
+ def validate(self, value):
+ """
+ If the value is lower than the limit, this function raises an error.
+
+ @raise LowerLimitConstraintError When the value is strictly smaller
+ than the limit.
+ """
+ if self.limit is not None:
+ if value >= self.limit:
+ return
+ raise LowerLimitConstraintError()
+ return
+
+class SizeConstraintError(Exception):
+ pass
+
+class SizeConstraint(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 SizeConstraintError("Setter : trying to set value of length %d while limit is %d"%(len(value), self.limit))
+ return
+
+class ColorConstraintError(Exception):
+ pass
+
+class ColorArraySizeError(ColorConstraintError):
+ pass
+
+class ColorTypeError(ColorConstraintError):
+ pass
+
+class ColorValueError(ColorConstraintError):
+ pass
+
+class ColorConstraint(Constraint):
+ """
+ Validates that the value is an array of size 3 with three numbers between
+ 0 and 255 (inclusively) in it.
+
+ """
+ def validate(self, value):
+ if len(value) != 3:
+ raise ColorArraySizeError("The value is not an array of size 3")
+
+ if not (type(value[0]) == type(22) and type(value[1]) == type(22) and type(value[2]) == type(22)):
+ raise ColorTypeError("Not all the elements of the array are integers")
+
+ if value[0] > 255 or value[0] <0:
+ raise ColorValueError("Red value is not between 0 and 255")
+
+ if value[1] > 255 or value[1] <0:
+ raise ColorValueError("Green value is not between 0 and 255")
+
+ if value[2] > 255 or value[2] <0:
+ raise ColorValueError("Blue value is not between 0 and 255")
+
+ return
+
+class BooleanConstraintError(Exception):
+ pass
+
+class BooleanConstraint(Constraint):
+ """
+ Validates that the value is either True or False.
+ """
+ def validate(self, value):
+ if value == True or value == False:
+ return
+ raise BooleanConstraintError("Value is not True or False")
+
+class EnumConstraintError(Exception):
+ pass
+
+class EnumConstraint(Constraint):
+ """
+ Validates that the value is part of a set of well-defined values.
+ """
+ def __init__(self, accepted_values):
+ """
+ Creates the constraint and stores the list of accepted values.
+
+ @param correct_values A list that contains all the values that will
+ be declared as satisfying the constraint
+ """
+ self._accepted_values = accepted_values
+
+ def validate(self, value):
+ """
+ Ensures that the value that is passed is part of the list of accepted
+ values.
+ """
+ if not value in self._accepted_values:
+ raise EnumConstraintError("Value is not part of the enumeration")
+ return
+
+class FileConstraintError(Exception):
+ pass
+
+class FileConstraint(Constraint):
+ """
+ Ensures that the string given corresponds to an existing file on disk.
+ """
+ def validate(self, value):
+ # TODO : Decide on the architecture for file retrieval on disk
+ # Relative paths? From where? Support macros?
+ #
+ if not os.path.isfile(value):
+ raise FileConstraintError("Non-existing file : %s"%value)
+ return
+ \ No newline at end of file
diff --git a/src/sugar/tutorius/properties.py b/src/sugar/tutorius/properties.py
new file mode 100644
index 0000000..5be7e1c
--- /dev/null
+++ b/src/sugar/tutorius/properties.py
@@ -0,0 +1,204 @@
+# 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
+
+from sugar.tutorius.constraints import *
+
+class TutoriusProperty():
+ """
+ The base class for all actions' properties. The interface is the following :
+
+ set() : attempts to change the value (may throw an exception if constraints
+ are not respected
+
+ value : the value of the property
+
+ type : the type of the property
+
+ get_contraints() : the constraints inserted on this property. They define
+ what is acceptable or not as values.
+ """
+ def __init__(self):
+ self._type = None
+ self._constraints = None
+ self._value = None
+
+ def set(self, value):
+ """
+ Attempts to set the value of the property. If the value does not respect
+ the constraints on the property, this method will raise an exception.
+
+ The exception should be of the type related to the constraint that
+ failed. E.g. When a int is to be set with a value that
+ """
+ for constraint_name in self.get_constraints():
+ constraint = getattr(self, constraint_name)
+ constraint.validate(value)
+ self._value = value
+ return True
+
+ def get(self):
+ return self._value
+
+ value = property(fget=get)
+
+ def get_constraints(self):
+ """
+ Returns the list of constraints associated to this property.
+ """
+ if self._constraints is None:
+ self._constraints = []
+ for i in dir(self):
+ t = getattr(self,i)
+ if isinstance(t, Constraint):
+ self._constraints.append(i)
+ return self._constraints
+
+ def get_type(self):
+ return self._type
+
+ type = property(fget=get_type)
+
+class TIntProperty(TutoriusProperty):
+ """
+ Represents an integer. Can have an upper value limit and/or a lower value
+ limit.
+ """
+
+ def __init__(self, value, lower_limit=None, upper_limit=None):
+ TutoriusProperty.__init__(self)
+ self._type = "int"
+ self.upper_limit = UpperLimitConstraint(upper_limit)
+ self.lower_limit = LowerLimitConstraint(lower_limit)
+
+ self.set(value)
+
+class TFloatProperty(TutoriusProperty):
+ """
+ Represents a floting point number. Can have an upper value limit and/or
+ a lower value limit.
+ """
+ def __init__(self, value, lower_limit=None, upper_limit=None):
+ TutoriusProperty.__init__(self)
+ self._type = "float"
+
+ self.upper_limit = UpperLimitConstraint(upper_limit)
+ self.lower_limit = LowerLimitConstraint(lower_limit)
+
+ self.set(value)
+
+class TStringProperty(TutoriusProperty):
+ """
+ Represents a string. Can have a maximum size limit.
+ """
+ def __init__(self, value, size_limit=None):
+ TutoriusProperty.__init__(self)
+ self._type = "string"
+ self.size_limit = SizeConstraint(size_limit)
+
+ self.set(value)
+
+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):
+ TutoriusProperty.__init__(self)
+ self._type = "array"
+ self.size_limit = SizeConstraint(size_limit)
+
+ self.set(value)
+
+class TColorProperty(TutoriusProperty):
+ """
+ Represents a RGB color with 3 8-bit integer values.
+
+ The value of the property is the array [R, G, B]
+ """
+ def __init__(self, red=None, green=None, blue=None):
+ TutoriusProperty.__init__(self)
+ self._type = "color"
+
+ self.color_constraint = ColorConstraint()
+
+ self._red = red or 0
+ self._green = green or 0
+ self._blue = blue or 0
+
+ self.set([self._red, self._green, self._blue])
+
+class TFileProperty(TutoriusProperty):
+ """
+ Represents a path to a file on the disk.
+ """
+ def __init__(self, path):
+ """
+ Defines the path to an existing file on disk file.
+
+ For now, the path may be relative or absolute, as long as it exists on
+ the local machine.
+ TODO : Make sure that we have a file scheme that supports distribution
+ on other computers (LP 355197)
+ """
+ TutoriusProperty.__init__(self)
+
+ self._type = "file"
+
+ self.file_constraint = FileConstraint()
+
+ self.set(path)
+
+class TEnumProperty(TutoriusProperty):
+ """
+ Represents a value in a given enumeration. This means that the value will
+ always be one in the enumeration and nothing else.
+
+ """
+ def __init__(self, value, accepted_values):
+ """
+ Creates the enumeration property.
+
+ @param value The initial value of the enum. Must be part of
+ accepted_values
+ @param accepted_values A list of values that the property can take
+ """
+ TutoriusProperty.__init__(self)
+
+ self._type = "enum"
+
+ self.enum_constraint = EnumConstraint(accepted_values)
+
+ self.set(value)
+
+class TBooleanProperty(TutoriusProperty):
+ """
+ Represents a True of False value.
+ """
+ def __init__(self, value=False):
+ TutoriusProperty.__init__(self)
+
+ self._type = "boolean"
+
+ self.boolean_constraint = BooleanConstraint()
+
+ self.set(value)
+
+class TUAMProperty(TutoriusProperty):
+ """
+ Represents a widget of the interface by storing its UAM.
+ """
+ # TODO : Pending UAM check-in (LP 355199)
+ pass
diff --git a/src/sugar/tutorius/tests/constraintstests.py b/src/sugar/tutorius/tests/constraintstests.py
new file mode 100644
index 0000000..407cc24
--- /dev/null
+++ b/src/sugar/tutorius/tests/constraintstests.py
@@ -0,0 +1,211 @@
+# 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
+
+from sugar.tutorius.constraints import *
+
+class ConstraintTest(unittest.TestCase):
+ def test_base_class(self):
+ cons = Constraint()
+ try:
+ cons.validate(1)
+ assert False, "Base class should throw an assertion"
+ except NotImplementedError:
+ pass
+
+class ValueConstraintTest(unittest.TestCase):
+ def test_limit_set(self):
+ cons = ValueConstraint(12)
+
+ assert cons.limit == 12
+
+class UpperLimitConstraintTest(unittest.TestCase):
+ def test_empty_constraint(self):
+ cons = UpperLimitConstraint(None)
+ try:
+ cons.validate(20)
+ except UpperLimitConstraintError:
+ assert False, "Empty contraint should not raise an exception"
+
+ def test_validate(self):
+ cons = UpperLimitConstraint(10)
+
+ try:
+ cons.validate(20)
+ assert False, "Validation of UpperLimit(10) on 20 should raise an exception"
+ except UpperLimitConstraintError:
+ pass
+
+ try:
+ cons.validate(5)
+ except UpperLimitConstraintError:
+ assert True, "Validation of UpperLimit(10) on 5 should not raise an exception"
+
+class LowerLimitConstraintTest(unittest.TestCase):
+ def test_empty_constraint(self):
+ cons = LowerLimitConstraint(None)
+ try:
+ cons.validate(20)
+ except LowerLimitConstraintError:
+ assert False, "Empty contraint should not raise an exception"
+
+ def test_validate(self):
+ cons = LowerLimitConstraint(10)
+
+ try:
+ cons.validate(5)
+ assert False, "Validation of LowerLimit(10) on 5 should raise an exception"
+ except LowerLimitConstraintError:
+ pass
+
+ try:
+ cons.validate(20)
+ except LowerLimitConstraintError:
+ assert True, "Validation of LowerLimit(10) on 20 should not raise an exception"
+
+class SizeConstraintTest(unittest.TestCase):
+ def test_empty_constraint(self):
+ cons = SizeConstraint(None)
+ try:
+ cons.validate(20)
+ except SizeConstraintError:
+ assert False, "Empty contraint should not raise an exception"
+
+ def test_validate(self):
+ cons = SizeConstraint(10)
+
+ try:
+ cons.validate(range(0, 20))
+ assert False, "Validation of SizeLimit(10) on list of length 20 should raise an exception"
+ except SizeConstraintError:
+ 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"
+
+class ColorConstraintTest(unittest.TestCase):
+ def test_validate(self):
+ cons = ColorConstraint()
+
+ try:
+ cons.validate([0, 0])
+ assert False, "ColorConstraint on list of length 2 should raise an exception"
+ except ColorArraySizeError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([0, 0, "str"])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorTypeError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([0, "str", 0])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorTypeError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate(["str", 0, 0])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorTypeError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([1, 2, 300])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorValueError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([1, -100, 30])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorValueError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([999999, 2, 300])
+ assert False, "ColorConstraint on with non-integers values should raise an exception"
+ except ColorValueError:
+ pass
+ except ColorConstraintError:
+ assert False, "ColorConstraint threw the wrong type of error"
+
+ try:
+ cons.validate([12, 23, 34])
+ except LowerLimitConstraintError:
+ assert True, "ColorConstraint on expected input should not raise an exception"
+
+class BooleanConstraintTest(unittest.TestCase):
+ def test_validate(self):
+ cons = BooleanConstraint()
+
+ cons.validate(True)
+ cons.validate(False)
+
+ try:
+ cons.validate(18)
+ assert False, "Setting integer on constraint should raise an error"
+ except BooleanConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type raised when setting wrong type"
+
+class EnumConstraintTest(unittest.TestCase):
+ def test_validate(self):
+ cons = EnumConstraint([1,2,3,7,8,9, "ex"])
+
+ cons.validate(8)
+
+ cons.validate("ex")
+
+ try:
+ cons.validate(4)
+ assert False, "There should be an exception on setting a value out of the enum"
+ except EnumConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type thrown"
+
+class FileConstraintTest(unittest.TestCase):
+ def test_validate(self):
+ cons = FileConstraint()
+
+ cons.validate("run-tests.py")
+
+ try:
+ cons.validate("unknown/file.py")
+ assert False, "Non-existing file check should throw an exception"
+ except FileConstraintError:
+ pass
+
+if __name__ == "__main__":
+ unittest.main() \ No newline at end of file
diff --git a/src/sugar/tutorius/tests/propertiestests.py b/src/sugar/tutorius/tests/propertiestests.py
new file mode 100644
index 0000000..52a9a75
--- /dev/null
+++ b/src/sugar/tutorius/tests/propertiestests.py
@@ -0,0 +1,348 @@
+# 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
+
+from sugar.tutorius.constraints import *
+from sugar.tutorius.properties import *
+
+# Helper function to test the wrong types on a property, given its type
+def try_wrong_values(prop):
+ if prop.type != "int":
+ try:
+ prop.set(3)
+ assert False, "Able to insert int value in property of type %s"%prop.type
+ except:
+ pass
+
+ if prop.type != "float":
+ try:
+ prop.set(1.1)
+ assert False, "Able to insert float value in property of type %s"%prop.type
+ except:
+ pass
+
+ if prop.type != "string":
+ try:
+ prop.set("Fake string")
+ assert False, "Able to insert string value in property of type %s"%prop.type
+ except:
+ pass
+
+ if prop.type != "array":
+ try:
+ prop.set([1, 2000, 3, 400])
+ assert False, "Able to insert array value in property of type %s"%prop.type
+ except:
+ pass
+
+ if prop.type != "color":
+ try:
+ prop.set([1,2,3])
+ if prop.type != "array":
+ assert False, "Able to insert color value in property of type %s"%prop.type
+ except:
+ pass
+
+ if prop.type != "boolean":
+ try:
+ prop.set(True)
+ if prop.type != "boolean":
+ assert False, "Able to set boolean value in property of type %s"%prop.type
+ except:
+ pass
+
+class BasePropertyTest(unittest.TestCase):
+ def test_base_class(self):
+ prop = TutoriusProperty()
+
+ assert prop.value == None, "There should not be an initial value in the base property"
+
+ assert prop.type == None, "There should be no type associated with the base property"
+
+ assert prop.get_constraints() == [], "There should be no constraints on the base property"
+
+ prop.set(2)
+
+ assert prop.value == 2, "Unable to set a value on base class"
+
+class TIntPropertyTest(unittest.TestCase):
+ def test_int_property(self):
+ prop = TIntProperty(22)
+
+ assert prop.value == 22, "Could not set value on property via constructor"
+
+ assert prop.type == "int", "Wrong type on int property : %s" % prop.type
+ cons = prop.get_constraints()
+ assert len(cons) == 2, "Not enough constraints on the int property"
+
+ prop.set(12)
+
+ assert prop.value == 12, "Could not set value"
+
+ def test_wrong_values(self):
+ prop = TIntProperty(33)
+
+ # Try setting values of other types
+ try_wrong_values(prop)
+
+ def test_limit_constructor(self):
+ prop = TIntProperty(22, 0, 30)
+
+ try:
+ prop.set(-22)
+ assert False, "Assigning an out-of-range value should trigger LowerLimitConstraint"
+ except LowerLimitConstraintError:
+ pass
+ except Exception:
+ assert False, "Wrong exception triggered by assignation"
+
+ try:
+ prop.set(222)
+ assert False, "Assigning an out-of-range value should trigger UpperLimitConstraint"
+ except UpperLimitConstraintError:
+ pass
+ except Exception:
+ assert False, "Wrong exception triggered by assignation"
+
+ def test_failing_constructor(self):
+ try:
+ prop = TIntProperty(100, 0, 20)
+ assert False, "Creation of the property should fail."
+ except UpperLimitConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+ try:
+ prop = TIntProperty(-100, 0, 20)
+ assert False, "Creation of the property should fail."
+ except LowerLimitConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+class TFloatPropertyTest(unittest.TestCase):
+ def test_float_property(self):
+ prop = TFloatProperty(22)
+
+ assert prop.value == 22, "Could not set value on property via constructor"
+
+ assert prop.type == "float", "Wrong type on float property : %s" % prop.type
+ cons = prop.get_constraints()
+ assert len(cons) == 2, "Not enough constraints on the float property"
+
+ prop.set(12)
+
+ assert prop.value == 12, "Could not set value"
+
+ def test_wrong_values(self):
+ prop = TFloatProperty(33)
+ # Try setting values of other types
+ try_wrong_values(prop)
+
+ def test_limit_constructor(self):
+ prop = TFloatProperty(22.4, 0.1, 30.5223)
+
+ try:
+ prop.set(-22.8)
+ assert False, "Assigning an out-of-range value should trigger LowerLimitConstraint"
+ except LowerLimitConstraintError:
+ pass
+ except Exception:
+ assert False, "Wrong exception triggered by assignation"
+
+ try:
+ prop.set(222.2)
+ assert False, "Assigning an out-of-range value should trigger UpperLimitConstraint"
+ except UpperLimitConstraintError:
+ pass
+ except Exception:
+ assert False, "Wrong exception triggered by assignation"
+
+ def test_failing_constructor(self):
+ try:
+ prop = TFloatProperty(100, 0, 20)
+ assert False, "Creation of the property should fail."
+ except UpperLimitConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+ try:
+ prop = TFloatProperty(-100, 0, 20)
+ assert False, "Creation of the property should fail."
+ except LowerLimitConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+class TStringPropertyTest(unittest.TestCase):
+ def test_basic_string(self):
+ prop = TStringProperty("Starter string")
+
+ assert prop.value == "Starter string", "Could not set string value via constructor"
+
+ assert prop.type == "string", "Wrong type for string property : %s" % prop.type
+
+ def test_size_limit(self):
+ prop = TStringProperty("Small", 10)
+
+ try:
+ prop.set("My string is too big!")
+ assert False, "String should not set to longer than max size"
+ except SizeConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type thrown"
+
+ def test_wrong_values(self):
+ prop = TStringProperty("Beginning")
+
+ try_wrong_values(prop)
+
+ def test_failing_constructor(self):
+ try:
+ prop = TStringProperty("This is normal", 5)
+ assert False, "Creation of the property should fail."
+ except SizeConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+class TArrayPropertyTest(unittest.TestCase):
+ def test_basic_array(self):
+ prop = TArrayProperty([1, 2, 3, 4])
+
+ assert prop.value == [1,2,3,4], "Unable to set initial value via constructor"
+
+ assert prop.type == "array", "Wrong type for array : %s"%prop.type
+
+ def test_wrong_values(self):
+ prop = TArrayProperty([1,2,3,4,5])
+
+ try_wrong_values(prop)
+
+ def test_size_limit(self):
+ prop = TArrayProperty([1,2], 4)
+
+ try:
+ prop.set([1,2,4,5,6,7])
+ assert False, "Size limit constraint was not properly applied"
+ except SizeConstraintError:
+ pass
+ except:
+ assert False, "Wrong type of exception thrown"
+
+
+ def test_failing_constructor(self):
+ try:
+ prop = TArrayProperty([100, 0, 20], 2)
+ assert False, "Creation of the property should fail."
+ except SizeConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+class TColorPropertyTest(unittest.TestCase):
+ def test_basic_color(self):
+ prop = TColorProperty(20, 40, 60)
+
+ assert prop.value == [20, 40, 60], "Could not set initial value with constructor"
+
+ assert prop.type == "color", "Wrong type on color : %s"%prop.type
+
+ def test_wrong_values(self):
+ prop = TColorProperty(250, 250, 250)
+
+ try_wrong_values(prop)
+
+ def test_failing_constructor(self):
+ try:
+ prop = TColorProperty(0, "str", 0)
+ assert False, "Creation of the property should fail."
+ except ColorTypeError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+class TBooleanPropertyTest(unittest.TestCase):
+ def setUp(self):
+ self.prop = TBooleanProperty(False)
+
+ def test_basic_boolean(self):
+ assert self.prop.value == False, "Could not set initial value via constructor"
+
+ self.prop.set(True)
+
+ assert self.prop.value == True, "Could not change the value via set"
+
+ self.prop.set(False)
+
+ assert self.prop.value == False, "Could not change the value via set"
+
+ def test_wrong_types(self):
+ try_wrong_values(self.prop)
+
+ def test_failing_constructor(self):
+ try:
+ prop = TBooleanProperty(64)
+ assert False, "Creation of the property should fail with non-boolean value"
+ except BooleanConstraintError:
+ pass
+ except:
+ assert False, "Wrong exception type on failed constructor"
+
+class TEnumPropertyTest(unittest.TestCase):
+ def setUp(self):
+ self.prop = TEnumProperty("hello", [1, 2, "hello", "world", True, None])
+
+ def test_basic_enum(self):
+ assert self.prop.value == "hello", "Could not set initial value on property"
+
+ self.prop.set(True)
+
+ assert self.prop.value, "Could not change the value via set"
+
+ try:
+ self.prop.set(4)
+ assert False, "Switched to a value outside the enum"
+ except EnumConstraintError:
+ pass
+
+ def test_wrong_type(self):
+ try_wrong_values(self.prop)
+
+class TFilePropertyTest(unittest.TestCase):
+ def setUp(self):
+ self.prop = TFileProperty("propertiestests.py")
+
+ def test_basic_file(self):
+ assert self.prop.value == "propertiestests.py", "Could not set initial value"
+
+ self.prop.set("run-tests.py")
+
+ assert self.prop.value == "run-tests.py", "Could not change value"
+
+ try:
+ self.prop.set("unknown/file/on/disk.gif")
+ assert False, "An exception should be thrown on unknown file"
+ except FileConstraintError:
+ pass
+
+if __name__ == "__main__":
+ unittest.main()
+
diff --git a/src/sugar/tutorius/tests/run-tests.py b/src/sugar/tutorius/tests/run-tests.py
index e1dfc00..1cda76e 100755
--- a/src/sugar/tutorius/tests/run-tests.py
+++ b/src/sugar/tutorius/tests/run-tests.py
@@ -37,6 +37,8 @@ if __name__=='__main__':
import linear_creatortests
import actiontests
import filterstests
+ import constraintstests
+ import propertiestests
suite = unittest.TestSuite()
suite.addTests(unittest.findTestCases(coretests))
@@ -46,6 +48,9 @@ if __name__=='__main__':
suite.addTests(unittest.findTestCases(linear_creatortests))
suite.addTests(unittest.findTestCases(actiontests))
suite.addTests(unittest.findTestCases(filterstests))
+ suite.addTests(unittest.findTestCases(constraintstests))
+ suite.addTests(unittest.findTestCases(propertiestests))
+
runner = unittest.TextTestRunner()
runner.run(suite)