From 065357ba57135e37fefff302db894e691924705d Mon Sep 17 00:00:00 2001 From: mike Date: Fri, 04 Dec 2009 22:25:14 +0000 Subject: Boy scout : Fixing tests for properties' values (cherry picked from commit 9f985564d6ade807a9182fcb7411388ed863e311) --- diff --git a/addons/gtkwidgettypefilter.py b/addons/gtkwidgettypefilter.py index 8faf172..0339e74 100644 --- a/addons/gtkwidgettypefilter.py +++ b/addons/gtkwidgettypefilter.py @@ -15,7 +15,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from ..filters import * -from ..properties import * +from ..properties import TStringProperty, TSequenceProperty from ..services import ObjectStore from ..gtkutils import find_widget @@ -28,7 +28,7 @@ class GtkWidgetTypeFilter(EventFilter): """ object_id = TStringProperty("") text = TStringProperty("") - strokes = TArrayProperty([]) + strokes = TSequenceProperty("") def __init__(self, object_id, text=None, strokes=None): """Constructor @@ -41,9 +41,9 @@ class GtkWidgetTypeFilter(EventFilter): """ super(GtkWidgetTypeFilter, self).__init__() self.object_id = object_id - self.text = text + self.text = text or "" self._captext = "" - self.strokes = strokes + self.strokes = strokes or [] self._capstrokes = [] self._widget = None self._handler_id = None @@ -86,9 +86,9 @@ class GtkWidgetTypeFilter(EventFilter): self._captext = self._captext + keystr logger.debug("~~~Current state: " + str(self._capstrokes) + ":" + str(self._captext)) - if not self.strokes is None and self.strokes in self._capstrokes: + if not self.strokes == [] and self.strokes in self._capstrokes: self.do_callback() - if not self.text is None and self.text in self._captext: + if not self.text is "" and self.text in self._captext: self.do_callback() __event__ = { diff --git a/tests/constraintstests.py b/tests/constraintstests.py index a5ccf26..f4318f8 100644 --- a/tests/constraintstests.py +++ b/tests/constraintstests.py @@ -36,6 +36,113 @@ class ValueConstraintTest(unittest.TestCase): assert cons.limit == 12 +def try_good_values(constraint, type): + if type == "int": + constraint.validate(1) + if type == "float": + constraint.validate(1.2) + if type == "str": + constraint.validate("This is a string") + if type == "tuple": + constraint.validate((1,2.0,'3',[4], (5,))) + if type == "list": + constraint.validate([1,2.0,'3',[4],(5,)]) + if type == "bool": + constraint.validate(True) + constraint.validate(False) + +def try_wrong_values(testcase, constraint, type): + if type != "int": + testcase.assertRaises(TypeConstraintError, constraint.validate, 1) + if type != "float": + testcase.assertRaises(TypeConstraintError, constraint.validate, 1.2) + if type != "str": + testcase.assertRaises(TypeConstraintError, constraint.validate, "1") + if type != "tuple": + testcase.assertRaises(TypeConstraintError, constraint.validate, (1,)) + if type != "list": + testcase.assertRaises(TypeConstraintError, constraint.validate, [1,]) + if type != "bool": + testcase.assertRaises(TypeConstraintError, constraint.validate, True) + +class TypeConstraintsTest(unittest.TestCase): + """ + Tests all the type constraints at the same time to ensure that they + only allow a single type of item. + """ + def test_int_type(self): + constraint = IntTypeConstraint() + try_good_values(constraint, 'int') + try_wrong_values(self, constraint, 'int') + + def test_float_type(self): + constraint = FloatTypeConstraint() + try_good_values(constraint, 'float') + try_wrong_values(self, constraint, 'float') + + def test_string_type(self): + constraint = StringTypeConstraint() + try_good_values(constraint, 'str') + try_wrong_values(self, constraint, 'str') + + def test_tuple_type(self): + constraint = TupleTypeConstraint() + try_good_values(constraint, 'tuple') + try_wrong_values(self, constraint, 'tuple') + + def test_list_type(self): + constraint = ListTypeConstraint() + try_good_values(constraint, 'list') + try_wrong_values(self, constraint, 'list') + + def test_bool_type(self): + constraint = BoolTypeConstraint() + try_good_values(constraint, 'bool') + try_wrong_values(self, constraint, 'bool') + +class MultiTypeConstraintsTest(unittest.TestCase): + """ + Tests all the type constraints at the same time to ensure that they + only allow the specified types of item. + """ + def _try_good_values(self, constraint, types_list): + if "int" in types_list: + constraint.validate(1) + if "float" in types_list: + constraint.validate(1.0) + if "str" in types_list: + constraint.validate("This is a string.") + if "tuple" in types_list: + constraint.validate((1, 2.0, '3', [4], (5,))) + if "list" in types_list: + constraint.validate([1, 2.0, '3', [4], (5,)]) + if "bool" in types_list: + constraint.validate(True) + + def _try_wrong_values(self, constraint, types_list): + if "int" not in types_list: + self.assertRaises(TypeConstraintError, constraint.validate, 1) + if "float" not in types_list: + self.assertRaises(TypeConstraintError, constraint.validate, 1.0) + if "str" not in types_list: + self.assertRaises(TypeConstraintError, constraint.validate, "1") + if "tuple" not in types_list: + self.assertRaises(TypeConstraintError, constraint.validate, (1,)) + if "list" not in types_list: + self.assertRaises(TypeConstraintError, constraint.validate, [1,]) + if "bool" not in types_list: + self.assertRaises(TypeConstraintError, constraint.validate, True) + + def test_array_type(self): + constraint = ArrayTypeConstraint() + self._try_good_values(constraint, ['tuple', 'list']) + self._try_wrong_values(constraint, ['tuple', 'list']) + + def test_sequence_type(self): + constraint = SequenceTypeConstraint() + self._try_good_values(constraint, ['list', 'tuple', 'str']) + self._try_wrong_values(constraint, ['list', 'tuple', 'str']) + class UpperLimitConstraintTest(unittest.TestCase): def test_empty_constraint(self): cons = UpperLimitConstraint(None) diff --git a/tests/propertiestests.py b/tests/propertiestests.py index 49d2312..55c3818 100644 --- a/tests/propertiestests.py +++ b/tests/propertiestests.py @@ -29,36 +29,35 @@ def try_wrong_values(obj): try: obj.prop = 3 assert False, "Able to insert int value in property of type %s"%typ - except ValueError: + except TypeConstraintError: pass if typ != "float": try: obj.prop = 1.1 assert False, "Able to insert float value in property of type %s"%typ - except ValueError: + except TypeConstraintError: pass - if typ != "string": + if typ != "string" and typ != "sequence": try: obj.prop = "Fake string" assert False, "Able to insert string value in property of type %s"%typ - except ValueError: + except TypeConstraintError: pass - if typ != "array": + if typ != "array" and typ != "color" and typ != "sequence": try: obj.prop = [1, 2000, 3, 400] assert False, "Able to insert array value in property of type %s"%typ - except ValueError: + except TypeConstraintError: pass - if typ != "color": + if typ != "color" and typ != "sequence" and typ != "array": try: obj.prop = [1,2,3] - if typ != "array": - assert False, "Able to insert color value in property of type %s"%typ - except ValueError: + assert False, "Able to insert color value in property of type %s"%typ + except TypeConstraintError: pass if typ != "boolean": @@ -66,7 +65,7 @@ def try_wrong_values(obj): obj.prop = True if typ != "boolean": assert False, "Able to set boolean value in property of type %s"%typ - except ValueError: + except TypeConstraintError: pass class BasePropertyTest(unittest.TestCase): @@ -216,7 +215,7 @@ class TIntPropertyTest(unittest.TestCase): assert klass.prop.type == "int", "Wrong type on int property : %s" % prop.type cons = klass.prop.get_constraints() - assert len(cons) == 2, "Not enough constraints on the int property" + assert len(cons) == 3, "Not enough constraints on the int property" obj.prop = 12 @@ -271,22 +270,22 @@ class TIntPropertyTest(unittest.TestCase): class TFloatPropertyTest(unittest.TestCase): def test_float_property(self): class klass(TPropContainer): - prop = TFloatProperty(22) + prop = TFloatProperty(22.0) obj = klass() - assert obj.prop == 22, "Could not set value on property via constructor" + assert obj.prop == 22.0, "Could not set value on property via constructor" assert klass.prop.type == "float", "Wrong type on float property : %s" % klass.prop.type cons = klass.prop.get_constraints() - assert len(cons) == 2, "Not enough constraints on the float property" + assert len(cons) == 3, "Not enough constraints on the float property" - obj.prop = 12 + obj.prop = 12.0 - assert obj.prop == 12, "Could not set value" + assert obj.prop == 12.0, "Could not set value" def test_wrong_values(self): class klass(TPropContainer): - prop = TFloatProperty(33) + prop = TFloatProperty(33.0) obj = klass() # Try setting values of other types @@ -315,7 +314,7 @@ class TFloatPropertyTest(unittest.TestCase): def test_failing_constructor(self): try: - prop = TFloatProperty(100, 0, 20) + prop = TFloatProperty(100.0, 0, 20) assert False, "Creation of the property should fail." except UpperLimitConstraintError: pass @@ -323,7 +322,7 @@ class TFloatPropertyTest(unittest.TestCase): assert False, "Wrong exception type on failed constructor" try: - prop = TFloatProperty(-100, 0, 20) + prop = TFloatProperty(-100.0, 0, 20) assert False, "Creation of the property should fail." except LowerLimitConstraintError: pass @@ -419,6 +418,56 @@ class TArrayPropertyTest(unittest.TestCase): except MinSizeConstraintError: pass +class TSequencePropertyTest(unittest.TestCase): + def test_basic_array(self): + class klass(TPropContainer): + prop = TSequenceProperty([1, 2, 3, 4]) + obj = klass() + + assert obj.prop == (1,2,3,4), "Unable to set initial value via constructor" + + assert klass.prop.type == "sequence", "Wrong type for sequence : %s"%klass.prop.type + + def test_wrong_values(self): + class klass(TPropContainer): + prop = TSequenceProperty([1,2,3,4,5]) + obj = klass() + + try_wrong_values(obj) + + def test_size_limits(self): + class klass(TPropContainer): + prop = TSequenceProperty([1,2], None, 4) + obj = klass() + + try: + obj.prop = [1,2,4,5,6,7] + assert False, "Maximum size limit constraint was not properly applied" + except MaxSizeConstraintError: + pass + + class klass(TPropContainer): + prop = TSequenceProperty([1,2,3,4], 2) + obj = klass() + + try: + obj.prop = [1] + assert False, "Minimum size limit constraint was not properly applied" + except MinSizeConstraintError: + pass + + def test_failing_constructor(self): + try: + prop = TSequenceProperty([100, 0, 20], None, 2) + assert False, "Creation of the property should fail." + except MaxSizeConstraintError: + pass + try: + prop = TSequenceProperty([100, 0, 20], 4, None) + assert False, "Creation of the property should fail." + except MinSizeConstraintError: + pass + class TColorPropertyTest(unittest.TestCase): def test_basic_color(self): class klass(TPropContainer): @@ -471,7 +520,7 @@ class TBooleanPropertyTest(unittest.TestCase): try: prop = TBooleanProperty(64) assert False, "Creation of the property should fail with non-boolean value" - except BooleanConstraintError: + except TypeConstraintError: pass except: assert False, "Wrong exception type on failed constructor" @@ -497,9 +546,6 @@ class TEnumPropertyTest(unittest.TestCase): except EnumConstraintError: pass - def test_wrong_type(self): - try_wrong_values(self.obj) - class TFilePropertyTest(unittest.TestCase): root_folder = "/tmp/tutorius" diff --git a/tutorius/constraints.py b/tutorius/constraints.py index cd71167..9ac1ca4 100644 --- a/tutorius/constraints.py +++ b/tutorius/constraints.py @@ -47,6 +47,96 @@ class Constraint(): """ raise NotImplementedError("Unable to validate a base Constraint") +class TypeConstraintError(ConstraintException): + pass + +class TypeConstraint(Constraint): + """ + Base class for ensuring that all the values have a specific Python type. + """ + def __init__(self, expected_type): + """ + Creates a new type constraint to restrict accepted values to a certain type. + @param expected_type A string describing the name of the type to enforce + """ + self._expected_type = expected_type + + def validate(self, value): + if self._expected_type == type(value).__name__: + return value + raise TypeConstraintError("Wrong type for value %s, got type %s; expected %s" % \ + (str(value), + type(value).__name__, + self._expected_type)) + +class IntTypeConstraint(TypeConstraint): + """ + Forces the value to be a Python int. + """ + def __init__(self): + TypeConstraint.__init__(self, 'int') + +class FloatTypeConstraint(TypeConstraint): + """ + Forces the value to be a Python float. + """ + def __init__(self): + TypeConstraint.__init__(self, 'float') + +class StringTypeConstraint(TypeConstraint): + """ + Forces the value to be a Python string. + """ + def __init__(self): + TypeConstraint.__init__(self, 'str') + +class TupleTypeConstraint(TypeConstraint): + """ + Forces the value to be a Python tuple. + """ + def __init__(self): + TypeConstraint.__init__(self, 'tuple') + +class ListTypeConstraint(TypeConstraint): + """ + Forces the value to be a Python list. + """ + def __init__(self): + TypeConstraint.__init__(self, 'list') + +class BoolTypeConstraint(TypeConstraint): + def __init__(self): + TypeConstraint.__init__(self, 'bool') + +class MultiTypeConstraint(Constraint): + """ + Base class for ensuring that all the values have a specific Python type. + """ + def __init__(self, expected_types_list): + """ + Creates a new type constraint to restrict accepted values to a certain type. + @param expected_types_list A list of strings describing the possible names + of the types to enforce. + """ + self._expected_types_list = expected_types_list + + def validate(self, value): + if type(value).__name__ in self._expected_types_list: + return value + raise TypeConstraintError("Wrong type for value %s, got type %s; expected one in %s" % \ + (str(value), + type(value).__name__, + str(self._expected_types_list))) + +class ArrayTypeConstraint(MultiTypeConstraint): + """ Forces the value to either be a list or a tuple.""" + def __init__(self): + MultiTypeConstraint.__init__(self, ['list', 'tuple']) + +class SequenceTypeConstraint(MultiTypeConstraint): + def __init__(self): + MultiTypeConstraint.__init__(self, ['list', 'tuple', 'str']) + class ValueConstraint(Constraint): """ A value constraint contains a _limit member that can be used in a children diff --git a/tutorius/properties.py b/tutorius/properties.py index 07b1645..4a80e9c 100644 --- a/tutorius/properties.py +++ b/tutorius/properties.py @@ -26,7 +26,9 @@ from .constraints import Constraint, \ UpperLimitConstraint, LowerLimitConstraint, \ MaxSizeConstraint, MinSizeConstraint, \ ColorConstraint, FileConstraint, BooleanConstraint, EnumConstraint, \ - ResourceConstraint + ResourceConstraint, IntTypeConstraint, FloatTypeConstraint, \ + StringTypeConstraint, ArrayTypeConstraint, BoolTypeConstraint, \ + SequenceTypeConstraint from .propwidgets import PropWidget, \ StringPropWidget, \ @@ -227,6 +229,7 @@ class TIntProperty(TutoriusProperty): self.type = "int" self.upper_limit = UpperLimitConstraint(upper_limit) self.lower_limit = LowerLimitConstraint(lower_limit) + self.int_type = IntTypeConstraint() self.default = self.validate(value) @@ -242,6 +245,7 @@ class TFloatProperty(TutoriusProperty): self.upper_limit = UpperLimitConstraint(upper_limit) self.lower_limit = LowerLimitConstraint(lower_limit) + self.float_type = FloatTypeConstraint() self.default = self.validate(value) @@ -254,6 +258,7 @@ class TStringProperty(TutoriusProperty): TutoriusProperty.__init__(self) self.type = "string" self.size_limit = MaxSizeConstraint(size_limit) + self.string_type = StringTypeConstraint() self.default = self.validate(value) @@ -268,6 +273,7 @@ class TArrayProperty(TutoriusProperty): self.type = "array" self.max_size_limit = MaxSizeConstraint(max_size_limit) self.min_size_limit = MinSizeConstraint(min_size_limit) + self.array_type = ArrayTypeConstraint() self.default = tuple(self.validate(value)) #Make this thing hashable @@ -283,6 +289,22 @@ class TArrayProperty(TutoriusProperty): value=self.value, ) +class TSequenceProperty(TutoriusProperty): + """ + Represents a data type that can be accessed with indices (via []). Those + are mainly for structures that only expect a list of elements that may + also be specified using a string. E.g. the strokes in a text type filter. + Since it is more convenient to specify a list of keystrokes as a string + rather than a list of characters, a sequence is the best option. + """ + def __init__(self, value, min_size_limit=None, max_size_limit=None): + TutoriusProperty.__init__(self) + self.type = "sequence" + self.max_size_limit = MaxSizeConstraint(max_size_limit) + self.min_size_limit = MinSizeConstraint(min_size_limit) + self.sequence_type = SequenceTypeConstraint() + self.default = tuple(self.validate(value)) + class TColorProperty(TutoriusProperty): """ Represents a RGB color with 3 8-bit integer values. @@ -293,6 +315,7 @@ class TColorProperty(TutoriusProperty): TutoriusProperty.__init__(self) self.type = "color" + self.array_type = ArrayTypeConstraint() self.color_constraint = ColorConstraint() self._red = red or 0 @@ -384,6 +407,7 @@ class TBooleanProperty(TutoriusProperty): self.type = "boolean" + self.bool_type = BoolTypeConstraint() self.boolean_constraint = BooleanConstraint() self.default = self.validate(value) diff --git a/tutorius/vault.py b/tutorius/vault.py index 1c1e33c..2b9c5b9 100644 --- a/tutorius/vault.py +++ b/tutorius/vault.py @@ -663,7 +663,6 @@ class XMLSerializer(Serializer): if getattr(type(comp), propname).type == "addonlist": compNode.appendChild(cls._create_addonlist_component_node(propname, propval, doc)) elif getattr(type(comp), propname).type == "addon": - #import rpdb2; rpdb2.start_embedded_debugger('pass') compNode.appendChild(cls._create_addon_component_node(propname, propval, doc)) else: # repr instead of str, as we want to be able to eval() it into a -- cgit v0.9.1