From 61b5b7a8488c2e3392905df3f6e7e02b7302fdd8 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 29 Aug 2008 03:17:26 +0000 Subject: 220: Renamed Option2.validate_scalar() to Option2.__validate_scalar(); added Option2.normalize() method; added corresponding unit tests --- ipalib/public.py | 39 +++++++++++---- ipalib/tests/test_public.py | 116 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 124 insertions(+), 31 deletions(-) diff --git a/ipalib/public.py b/ipalib/public.py index e0d9e6d28..147596e1d 100644 --- a/ipalib/public.py +++ b/ipalib/public.py @@ -86,9 +86,7 @@ class DefaultFrom(plugable.ReadOnly): class Option2(plugable.ReadOnly): def __init__(self, name, doc, type_, required=False, multivalue=False, - default=None, default_from=None, normalize=None, rules=tuple() - ): - + default=None, default_from=None, rules=tuple(), normalize=None): self.name = name self.doc = doc self.type = type_ @@ -100,7 +98,32 @@ class Option2(plugable.ReadOnly): self.rules = (type_.validate,) + rules lock(self) - def validate_scalar(self, value): + def convert(self, value): + if self.multivalue: + if type(value) in (tuple, list): + return tuple(self.type(v) for v in value) + return (self.type(value),) + return self.type(value) + + def __normalize_scalar(self, value): + if value is None: + return None + if type(value) is not self.type.type: + raise TypeError('need a %r; got %r' % (self.type.type, value)) + return self.__normalize(value) + + def normalize(self, value): + if self.__normalize is None: + return value + if self.multivalue: + if value is None: + return None + if type(value) is not tuple: + raise TypeError('multivalue must be a tuple; got %r' % value) + return tuple(self.__normalize_scalar(v) for v in value) + return self.__normalize_scalar(value) + + def __validate_scalar(self, value): for rule in self.rules: error = rule(value) if error is not None: @@ -109,13 +132,11 @@ class Option2(plugable.ReadOnly): def validate(self, value): if self.multivalue: if type(value) is not tuple: - raise TypeError( - 'when multivalue, value must be tuple; got %r' % value - ) + raise TypeError('multivalue must be a tuple; got %r' % value) for v in value: - self.validate_scalar(v) + self.__validate_scalar(v) else: - self.validate_scalar(value) + self.__validate_scalar(value) class Option(plugable.Plugin): diff --git a/ipalib/tests/test_public.py b/ipalib/tests/test_public.py index ed4c75e59..b34e48757 100644 --- a/ipalib/tests/test_public.py +++ b/ipalib/tests/test_public.py @@ -114,6 +114,9 @@ class test_Option2(ClassChecker): assert self.cls.__bases__ == (plugable.ReadOnly,) def test_init(self): + """ + Tests the `public.Option2.__init__` method. + """ name = 'sn', doc = 'Last Name', type_ = ipa_types.Unicode() @@ -128,8 +131,82 @@ class test_Option2(ClassChecker): assert read_only(o, 'default_from') is None assert read_only(o, 'rules') == (type_.validate,) + def test_convert(self): + name = 'sn' + doc = 'User last name' + type_ = ipa_types.Unicode() + class Hello(object): + def __unicode__(self): + return u'hello' + hello = Hello() + values = (u'hello', 'hello', hello) + # Test when multivalue=False: + o = self.cls(name, doc, type_) + for value in values: + new = o.convert(value) + assert new == u'hello' + assert type(new) is unicode + # Test when multivalue=True: + o = self.cls(name, doc, type_, multivalue=True) + for value in values: + for v in (value, (value,)): + new = o.convert(hello) + assert new == (u'hello',) + assert type(new) is tuple + + def test_normalize(self): + """ + Tests the `public.Option2.validate` method. + """ + name = 'sn' + doc = 'User last name' + t = ipa_types.Unicode() + callback = lambda value: value.lower() + orig = u'Hello World' + orig_str = str(orig) + norm = u'hello world' + tup_orig = (orig, norm, u'WONDERFUL!') + tup_norm = (norm, norm, u'wonderful!') + tup_str = (orig_str, orig) + all_values = (None, orig, orig_str, norm, tup_orig, tup_norm, tup_str) + + ## Scenario 1: multivalue=False, normalize=None + o = self.cls(name, doc, t) + for v in all_values: + # When normalize=None, value is returned, no type checking: + assert o.normalize(v) is v + + ## Scenario 2: multivalue=False, normalize=callback + o = self.cls(name, doc, t, normalize=callback) + assert o.normalize(None) is None + for v in (orig, norm): + assert o.normalize(v) == norm + for v in (orig_str, tup_orig, tup_norm, tup_str): # Not unicode + e = raises(TypeError, o.normalize, v) + assert str(e) == 'need a %r; got %r' % (unicode, v) + + ## Scenario 3: multivalue=True, normalize=None + o = self.cls(name, doc, t, multivalue=True) + for v in all_values: + # When normalize=None, value is returned, no type checking: + assert o.normalize(v) is v + + ## Scenario 4: multivalue=True, normalize=callback + o = self.cls(name, doc, t, multivalue=True, normalize=callback) + assert o.normalize(None) is None + for v in (tup_orig, tup_norm): + assert o.normalize(v) == tup_norm + for v in (orig, orig_str, norm): # Not tuple + e = raises(TypeError, o.normalize, v) + assert str(e) == 'multivalue must be a tuple; got %r' % v + for v in [tup_str, (norm, orig, orig_str)]: # Not unicode + e = raises(TypeError, o.normalize, v) + assert str(e) == 'need a %r; got %r' % (unicode, orig_str) + def test_validate(self): - # Constructor arguments + """ + Tests the `public.Option2.validate` method. + """ name = 'sn' doc = 'User last name' type_ = ipa_types.Unicode() @@ -137,36 +214,31 @@ class test_Option2(ClassChecker): if not value.islower(): return 'Must be lower case' my_rules = (case_rule,) - - # Some test values: okay = u'whatever' fail_case = u'Whatever' fail_type = 'whatever' - # Test validate() and validate_scalar() when multivalue=False: + ## Scenario 1: multivalue=False o = self.cls(name, doc, type_, rules=my_rules) assert o.rules == (type_.validate, case_rule) - for m in [o.validate, o.validate_scalar]: - # Test a valid value: - m(okay) - # Check that RuleError is raised with wrong case: - e = raises(errors.RuleError, m, fail_case) - assert e.name is name - assert e.value is fail_case - assert e.error == 'Must be lower case' - # Test a RuleError is raise with wrong type: - e = raises(errors.RuleError, m, fail_type) - assert e.name is name - assert e.value is fail_type - assert e.error == 'Must be a string' + # Test a valid value: + o.validate(okay) + # Check that RuleError is raised with wrong case: + e = raises(errors.RuleError, o.validate, fail_case) + assert e.name is name + assert e.value is fail_case + assert e.error == 'Must be lower case' + # Test a RuleError is raise with wrong type: + e = raises(errors.RuleError, o.validate, fail_type) + assert e.name is name + assert e.value is fail_type + assert e.error == 'Must be a string' - # Test validate() when multivalue=True: + ## Scenario 2: multivalue=True o = self.cls(name, doc, type_, multivalue=True, rules=my_rules) def check_type_error(value): e = raises(TypeError, o.validate, value) - assert str(e) == ( - 'when multivalue, value must be tuple; got %r' % value - ) + assert str(e) == 'multivalue must be a tuple; got %r' % value # Check a valid value: check_type_error(okay) o.validate((okay,)) @@ -177,7 +249,7 @@ class test_Option2(ClassChecker): assert e.name is name assert e.value is fail_case assert e.error == 'Must be lower case' - # Test a RuleError is raise with wrong type: + # Check that RuleError is raise with wrong type: check_type_error(fail_type) for value in [(okay, fail_type), (fail_type, okay)]: e = raises(errors.RuleError, o.validate, value) -- cgit