diff --git a/test/test_choice_option.py b/test/test_choice_option.py index 8565be9..7469d9b 100644 --- a/test/test_choice_option.py +++ b/test/test_choice_option.py @@ -22,6 +22,10 @@ def return_calc_list(val): return [val] +def return_error(): + raise Exception('test') + + def test_choiceoption_function(): ch = ChoiceOption('ch', '', values=return_list) od = OptionDescription('od', '', [ch]) @@ -39,6 +43,14 @@ def test_choiceoption_function(): assert ch.impl_get_values(cfg) == ['val1', 'val2'] +def test_choiceoption_function_error(): + ch = ChoiceOption('ch', '', values=return_error) + od = OptionDescription('od', '', [ch]) + cfg = Config(od) + cfg.read_write() + raises(Exception, "cfg.ch = 'no'") + + def test_choiceoption_calc_function(): ch = ChoiceOption('ch', "", values=return_calc_list, values_params={'': ('val1',)}) od = OptionDescription('od', '', [ch]) @@ -70,6 +82,15 @@ def test_choiceoption_calc_opt_function(): assert cfg.getowner(ch) == owners.default +def test_choiceoption_calc_opt_function_propertyerror(): + st = StrOption('st', '', 'val1', properties=('disabled',)) + ch = ChoiceOption('ch', "", values=return_calc_list, values_params={'': ((st, False),)}) + od = OptionDescription('od', '', [st, ch]) + cfg = Config(od) + cfg.read_write() + raises(ValueError, "cfg.ch='no'") + + def test_choiceoption_calc_opt_multi_function(): st = StrOption('st', '', ['val1'], multi=True) ch = ChoiceOption('ch', "", default_multi='val2', values=return_val, values_params={'': ((st, False),)}, multi=True) diff --git a/test/test_config_domain.py b/test/test_config_domain.py index 8225187..528efda 100644 --- a/test/test_config_domain.py +++ b/test/test_config_domain.py @@ -146,6 +146,7 @@ def test_email(): c.e = u'foo-bar.baz@example.com' c.e = u'root@foo.com' c.e = u'root@domain' + raises(ValueError, "c.e = 1") raises(ValueError, "c.e = u'root'") raises(ValueError, "c.e = u'root[]@domain'") @@ -158,6 +159,7 @@ def test_url(): c.u = 'http://foo.com' c.u = 'https://foo.com' c.u = 'https://foo.com/' + raises(ValueError, "c.u = 1") raises(ValueError, "c.u = 'ftp://foo.com'") raises(ValueError, "c.u = 'foo.com'") raises(ValueError, "c.u = ':/foo.com'") diff --git a/test/test_config_ip.py b/test/test_config_ip.py index dfcb4af..3f15b02 100644 --- a/test/test_config_ip.py +++ b/test/test_config_ip.py @@ -5,7 +5,7 @@ import warnings from py.test import raises from tiramisu.config import Config from tiramisu.option import IPOption, NetworkOption, NetmaskOption, \ - PortOption, OptionDescription + PortOption, BroadcastOption, OptionDescription from tiramisu.error import ValueWarning @@ -26,6 +26,7 @@ def test_ip(): raises(ValueError, "c.b = '88.88.88.88'") c.b = '0.0.0.0' raises(ValueError, "c.b = '255.255.255.0'") + raises(ValueError, "c.a = '333.0.1.20'") raises(ValueError, "IPOption('a', 'ip', default='192.000.023.01')") with warnings.catch_warnings(record=True) as w: @@ -64,7 +65,11 @@ def test_network(): c.a = '192.168.1.0' c.a = '88.88.88.88' c.a = '0.0.0.0' + raises(ValueError, "c.a = 1") + raises(ValueError, "c.a = '1.1.1.1.1'") raises(ValueError, "c.a = '255.255.255.0'") + raises(ValueError, "c.a = '192.168.001.0'") + raises(ValueError, "c.a = '333.168.1.1'") with warnings.catch_warnings(record=True) as w: c.b = '255.255.255.0' assert len(w) == 1 @@ -78,9 +83,25 @@ def test_netmask(): a = NetmaskOption('a', '') od = OptionDescription('od', '', [a]) c = Config(od) + raises(ValueError, "c.a = '192.168.1.1.1'") raises(ValueError, "c.a = '192.168.1.1'") raises(ValueError, "c.a = '192.168.1.0'") raises(ValueError, "c.a = '88.88.88.88'") + raises(ValueError, "c.a = '255.255.255.000'") + raises(ValueError, "c.a = 2") + c.a = '0.0.0.0' + c.a = '255.255.255.0' + + +def test_broadcast(): + a = BroadcastOption('a', '') + od = OptionDescription('od', '', [a]) + c = Config(od) + raises(ValueError, "c.a = '192.168.1.255.1'") + raises(ValueError, "c.a = '192.168.001.255'") + raises(ValueError, "c.a = '192.168.0.300'") + raises(ValueError, "c.a = 1") + raises(ValueError, "c.a = 2") c.a = '0.0.0.0' c.a = '255.255.255.0' diff --git a/test/test_option_type.py b/test/test_option_type.py index c80cc83..2da3e92 100644 --- a/test/test_option_type.py +++ b/test/test_option_type.py @@ -7,7 +7,7 @@ from py.test import raises from tiramisu.config import Config from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ - StrOption, OptionDescription + PasswordOption, StrOption, DateOption, OptionDescription from tiramisu.error import PropertiesOptionError @@ -133,3 +133,28 @@ def test_with_many_subgroups(): assert name == "booltwo" getattr(homeconfig.cfgimpl_get_description(), name) assert 'hidden' in setting[booltwo] + + +def test_password_option(): + o = PasswordOption('o', '') + d = OptionDescription('d', '', [o]) + c = Config(d) + + c.o = u'a_valid_password' + raises(ValueError, "c.o = 1") + + +def test_date_option(): + o = DateOption('o', '') + d = OptionDescription('d', '', [o]) + c = Config(d) + + c.o = u'2017-02-04' + c.o = u'2017-2-4' + raises(ValueError, "c.o = 1") + raises(ValueError, "c.o = u'2017-13-20'") + raises(ValueError, "c.o = u'2017-11-31'") + raises(ValueError, "c.o = u'2017-12-32'") + raises(ValueError, "c.o = u'2017-2-29'") + raises(ValueError, "c.o = u'2-2-2017'") + raises(ValueError, "c.o = u'2017/2/2'") diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 5b72236..600cf2a 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -53,7 +53,7 @@ class ChoiceOption(Option): else: if values_params is not None: raise ValueError(_('values is not a function, so values_params must be None')) - if not isinstance(values, tuple): # pragma: optional cover + if not isinstance(values, tuple): raise TypeError(_('values must be a tuple or a function for {0}' ).format(name)) session = self.getsession() @@ -74,23 +74,18 @@ class ChoiceOption(Option): def impl_get_values(self, context, current_opt=undefined): if current_opt is undefined: current_opt = self - params = undefined #FIXME cache? but in context... values = self._choice_values if isinstance(values, FunctionType): if context is None: values = [] else: - if params is not undefined: - values_params = params - else: - values_params = self.impl_get_choice_values_params() values = carry_out_calculation(current_opt, context=context, callback=values, - callback_params=values_params) + callback_params=self.impl_get_choice_values_params()) if isinstance(values, Exception): return values - if values is not undefined and not isinstance(values, list): # pragma: optional cover + if values is not undefined and not isinstance(values, list): raise ConfigError(_('calculated values for {0} is not a list' '').format(self.impl_getname())) return values @@ -100,7 +95,7 @@ class ChoiceOption(Option): values = self.impl_get_values(context, current_opt=current_opt) if isinstance(values, Exception): return values - if values is not undefined and not value in values: # pragma: optional cover + if values is not undefined and not value in values: if len(values) == 1: return ValueError(_('only {0} is allowed' '').format(values[0])) @@ -116,7 +111,7 @@ class BoolOption(Option): def _validate(self, value, context=undefined, current_opt=undefined): if not isinstance(value, bool): - return ValueError() # pragma: optional cover + return ValueError() class IntOption(Option): @@ -126,7 +121,7 @@ class IntOption(Option): def _validate(self, value, context=undefined, current_opt=undefined): if not isinstance(value, int): - return ValueError() # pragma: optional cover + return ValueError() class FloatOption(Option): @@ -136,7 +131,7 @@ class FloatOption(Option): def _validate(self, value, context=undefined, current_opt=undefined): if not isinstance(value, float): - return ValueError() # pragma: optional cover + return ValueError() class StrOption(Option): @@ -146,10 +141,10 @@ class StrOption(Option): def _validate(self, value, context=undefined, current_opt=undefined): if not isinstance(value, str): - return ValueError() # pragma: optional cover + return ValueError() -if sys.version_info[0] >= 3: # pragma: optional cover +if sys.version_info[0] >= 3: # pragma: no cover #UnicodeOption is same as StrOption in python 3+ class UnicodeOption(StrOption): __slots__ = tuple() @@ -163,7 +158,7 @@ else: def _validate(self, value, context=undefined, current_opt=undefined): if not isinstance(value, unicode): - return ValueError() # pragma: optional cover + return ValueError() class PasswordOption(Option): @@ -211,22 +206,22 @@ class IPOption(Option): return ValueError() for val in value.split('.'): if val.startswith("0") and len(val) > 1: - return ValueError() # pragma: optional cover + return ValueError() # 'standard' validation try: IP('{0}/32'.format(value)) - except ValueError: # pragma: optional cover + except ValueError: return ValueError() def _second_level_validation(self, value, warnings_only): ip = IP('{0}/32'.format(value)) - if not self._get_extra('_allow_reserved') and ip.iptype() == 'RESERVED': # pragma: optional cover + if not self._get_extra('_allow_reserved') and ip.iptype() == 'RESERVED': if warnings_only: msg = _("shouldn't in reserved class") else: msg = _("mustn't be in reserved class") return ValueError(msg) - if self._get_extra('_private_only') and not ip.iptype() == 'PRIVATE': # pragma: optional cover + if self._get_extra('_private_only') and not ip.iptype() == 'PRIVATE': if warnings_only: msg = _("should be in private class") else: @@ -235,11 +230,11 @@ class IPOption(Option): def _cons_in_network(self, current_opt, opts, vals, warnings_only): if len(vals) != 3: - raise ConfigError(_('invalid len for vals')) # pragma: optional cover + raise ConfigError(_('invalid len for vals')) if None in vals: return ip, network, netmask = vals - if IP(ip) not in IP('{0}/{1}'.format(network, netmask)): # pragma: optional cover + if IP(ip) not in IP('{0}/{1}'.format(network, netmask)): if warnings_only: msg = _('should be in network {0}/{1} ({2}/{3})') else: @@ -286,12 +281,12 @@ class PortOption(Option): elif not allowed: is_finally = True elif allowed and is_finally: - raise ValueError(_('inconsistency in allowed range')) # pragma: optional cover + raise ValueError(_('inconsistency in allowed range')) if allowed: extra['_max_value'] = ports_max[index] if extra['_max_value'] is None: - raise ValueError(_('max value is empty')) # pragma: optional cover + raise ValueError(_('max value is empty')) super(PortOption, self).__init__(name, doc, default=default, default_multi=default_multi, @@ -307,14 +302,14 @@ class PortOption(Option): def _validate(self, value, context=undefined, current_opt=undefined): if isinstance(value, int): - if sys.version_info[0] >= 3: # pragma: optional cover + if sys.version_info[0] >= 3: # pragma: no cover value = str(value) else: value = unicode(value) err = self._impl_valid_unicode(value) if err: return err - if self._get_extra('_allow_range') and ":" in str(value): # pragma: optional cover + if self._get_extra('_allow_range') and ":" in str(value): value = str(value).split(':') if len(value) != 2: return ValueError(_('range must have two values only')) @@ -328,7 +323,7 @@ class PortOption(Option): if not self.port_re.search(val): return ValueError() val = int(val) - if not self._get_extra('_min_value') <= val <= self._get_extra('_max_value'): # pragma: optional cover + if not self._get_extra('_min_value') <= val <= self._get_extra('_max_value'): return ValueError(_('must be an integer between {0} ' 'and {1}').format(self._get_extra('_min_value'), self._get_extra('_max_value'))) @@ -350,12 +345,12 @@ class NetworkOption(Option): return ValueError() try: IP(value) - except ValueError: # pragma: optional cover + except ValueError: return ValueError() def _second_level_validation(self, value, warnings_only): ip = IP(value) - if ip.iptype() == 'RESERVED': # pragma: optional cover + if ip.iptype() == 'RESERVED': if warnings_only: msg = _("shouldn't be in reserved class") else: @@ -379,7 +374,7 @@ class NetmaskOption(Option): return ValueError() try: IP('0.0.0.0/{0}'.format(value)) - except ValueError: # pragma: optional cover + except ValueError: return ValueError() def _cons_network_netmask(self, current_opt, opts, vals, warnings_only): @@ -397,7 +392,7 @@ class NetmaskOption(Option): def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net, warnings_only): if len(opts) != 2: - return ConfigError(_('invalid len for opts')) # pragma: optional cover + return ConfigError(_('invalid len for opts')) msg = None try: ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask), @@ -410,10 +405,10 @@ class NetmaskOption(Option): if ip.broadcast() == val_ip: msg = _("this is a broadcast with netmask {0} ({1})") - except ValueError: # pragma: optional cover + except ValueError: if not make_net: msg = _('with netmask {0} ({1})') - if msg is not None: # pragma: optional cover + if msg is not None: return ValueError(msg.format(val_netmask, opts[1].impl_getname())) @@ -425,20 +420,25 @@ class BroadcastOption(Option): err = self._impl_valid_unicode(value) if err: return err + if value.count('.') != 3: + return ValueError() + for val in value.split('.'): + if val.startswith("0") and len(val) > 1: + return ValueError() try: IP('{0}/32'.format(value)) - except ValueError: # pragma: optional cover + except ValueError: return ValueError() def _cons_broadcast(self, current_opt, opts, vals, warnings_only): if len(vals) != 3: - raise ConfigError(_('invalid len for vals')) # pragma: optional cover + raise ConfigError(_('invalid len for vals')) if None in vals: return broadcast, network, netmask = vals if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast): return ValueError(_('with network {0}/{1} ({2}/{3})').format( - network, netmask, opts[1].impl_getname(), opts[2].impl_getname())) # pragma: optional cover + network, netmask, opts[1].impl_getname(), opts[2].impl_getname())) class DomainnameOption(Option): @@ -457,12 +457,12 @@ class DomainnameOption(Option): properties=None, allow_ip=False, type_='domainname', warnings_only=False, allow_without_dot=False): if type_ not in ['netbios', 'hostname', 'domainname']: - raise ValueError(_('unknown type_ {0} for hostname').format(type_)) # pragma: optional cover + raise ValueError(_('unknown type_ {0} for hostname').format(type_)) extra = {'_dom_type': type_} if allow_ip not in [True, False]: - raise ValueError(_('allow_ip must be a boolean')) # pragma: optional cover + raise ValueError(_('allow_ip must be a boolean')) if allow_without_dot not in [True, False]: - raise ValueError(_('allow_without_dot must be a boolean')) # pragma: optional cover + raise ValueError(_('allow_without_dot must be a boolean')) extra['_allow_ip'] = allow_ip extra['_allow_without_dot'] = allow_without_dot extra['_domain_re'] = re.compile(r'^[a-z\d][a-z\d\-]*$') @@ -492,7 +492,7 @@ class DomainnameOption(Option): return ValueError(_("invalid length (max {0})" "").format(part_name_length)) - if self._get_extra('_allow_ip') is True: # pragma: optional cover + if self._get_extra('_allow_ip') is True: try: IP('{0}/32'.format(value)) return @@ -555,7 +555,7 @@ class URLOption(DomainnameOption): if err: return err match = self.proto_re.search(value) - if not match: # pragma: optional cover + if not match: return ValueError(_('must start with http:// or ' 'https://')) value = value[len(match.group(0)):] @@ -575,7 +575,7 @@ class URLOption(DomainnameOption): domain, port = splitted if not 0 <= int(port) <= 65535: return ValueError(_('port must be an between 0 and ' - '65536')) # pragma: optional cover + '65536')) # validate domainname err = super(URLOption, self)._validate(domain) if err: @@ -585,7 +585,7 @@ class URLOption(DomainnameOption): return err # validate file if files is not None and files != '' and not self.path_re.search(files): - return ValueError(_('must ends with a valid resource name')) # pragma: optional cover + return ValueError(_('must ends with a valid resource name')) def _second_level_validation(self, value, warnings_only): pass