From 29177ac24ce70208a6225669a1275f67ac0f647a Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 06 Apr 2009 17:24:41 +0000 Subject: Merge branch 'master' of ssh://mike@bobthebuilder.mine.nu:8080/home/git --- 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) -- cgit v0.9.1