some improvements
[tiramisu.git] / test / test_option_consistency.py
index a7e8370..a5fedc4 100644 (file)
-import autopath
+from autopath import do_autopath
+do_autopath()
+
 from py.test import raises
 
-from tiramisu.config import *
-from tiramisu.option import *
-
-def make_description():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
-    gcdummy = BoolOption('dummy', 'dummy', default=False)
-    objspaceoption = ChoiceOption('objspace', 'Object space',
-                                ['std', 'thunk'], 'std')
-    booloption = BoolOption('bool', 'Test boolean option', default=True)
-    intoption = IntOption('int', 'Test int option', default=0)
-    floatoption = FloatOption('float', 'Test float option', default=2.3)
-    stroption = StrOption('str', 'Test string option', default="abc")
-    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
-    wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                    requires=['boolop'])
-    wantframework_option = BoolOption('wantframework', 'Test requires',
-                                      default=False,
-                                      requires=['boolop'])
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
-    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
-                                           wantref_option, stroption,
-                                           wantframework_option,
-                                           intoption, boolop])
-    return descr
-
-def make_description_duplicates():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
-    ## dummy 1
-    gcdummy = BoolOption('dummy', 'dummy', default=False)
-    objspaceoption = ChoiceOption('objspace', 'Object space',
-                                ['std', 'thunk'], 'std')
-    booloption = BoolOption('bool', 'Test boolean option', default=True)
-    intoption = IntOption('int', 'Test int option', default=0)
-    floatoption = FloatOption('float', 'Test float option', default=2.3)
-    stroption = StrOption('str', 'Test string option', default="abc")
-    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
-    wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                    requires=['boolop'])
-    wantframework_option = BoolOption('wantframework', 'Test requires',
-                                      default=False,
-                                      requires=['boolop'])
-    # dummy2 (same name)
-    gcdummy2  = BoolOption('dummy', 'dummy2', default=True)
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, gcdummy2, floatoption])
-    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
-                                           wantref_option, stroption,
-                                           wantframework_option,
-                                           intoption, boolop])
-    return descr
-
-def test_identical_names():
-    """If in the schema (the option description) there is something that 
-    have the same name, an exection is raised
-    """    
-    descr = make_description_duplicates()
-    raises(ConflictConfigError, "cfg = Config(descr)")
-
-def make_description2():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
-    gcdummy = BoolOption('dummy', 'dummy', default=False)
-
-    floatoption = FloatOption('float', 'Test float option', default=2.3)
-    
-    objspaceoption = ChoiceOption('objspace', 'Object space',
-                                ['std', 'thunk'], 'std')
-    booloption = BoolOption('bool', 'Test boolean option', default=True)
-    intoption = IntOption('int', 'Test int option', default=0)
-    stroption = StrOption('str', 'Test string option', default="abc")
-    # first multi
-    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
-    boolop.enable_multi()
-    wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                    requires=['boolop'])
-    # second multi
-    wantframework_option = BoolOption('wantframework', 'Test requires',
-                                      default=False,
-                                      requires=['boolop'])
-    wantframework_option.enable_multi()
-    
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
-    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
-                                           wantref_option, stroption,
-                                           wantframework_option,
-                                           intoption, boolop])
-    return descr
-
-# FIXME: XXX would you mind putting the multi validations anywhere else 
-# than in the requires !!! 
-#def test_multi_constraints():
-#    "a multi in a constraint has to have the same length"
-#    descr = make_description2()
-#    cfg = Config(descr)
-#    cfg.boolop = [True, True, False]
-#    cfg.wantframework = [False, False, True]
-#    
-#def test_multi_raise():
-#    "a multi in a constraint has to have the same length"
-#    # FIXME fusionner les deux tests, MAIS PROBLEME :
-#    # il ne devrait pas etre necessaire de refaire une config
-#    # si la valeur est modifiee une deuxieme fois -> 
-#    #raises(ConflictConfigError, "cfg.wantframework = [False, False, True]")
-#    #   ExceptionFailure: 'DID NOT RAISE'
-#    descr = make_description2()
-#    cfg = Config(descr)
-#    cfg.boolop = [True]
-#    raises(ConflictConfigError, "cfg.wantframework = [False, False, True]")
-# ____________________________________________________________
-# adding dynamically new options description schema
-def test_newoption_add_in_descr():
-    descr = make_description()
-    newoption = BoolOption('newoption', 'dummy twoo', default=False)
-    descr.add_child(newoption)
-    config = Config(descr)
-    assert config.newoption == False
-
-def test_newoption_add_in_subdescr():
-    descr = make_description()
-    newoption = BoolOption('newoption', 'dummy twoo', default=False)
-    descr.gc.add_child(newoption)
-    config = Config(descr, bool=False)
-    assert config.gc.newoption == False
-
-def test_newoption_add_in_config():
-    descr = make_description()
-    config = Config(descr, bool=False)
-    newoption = BoolOption('newoption', 'dummy twoo', default=False)
-    descr.add_child(newoption)
-    config.cfgimpl_update()
-    assert config.newoption == False
-# ____________________________________________________________
-def make_description_requires():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
-    gcdummy = BoolOption('dummy', 'dummy', default=False)
-
-    floatoption = FloatOption('float', 'Test float option', default=2.3)
-    
-    objspaceoption = ChoiceOption('objspace', 'Object space',
-                                ['std', 'thunk'], 'std')
-    booloption = BoolOption('bool', 'Test boolean option', default=True)
-    intoption = IntOption('int', 'Test int option', default=0)
-    stroption = StrOption('str', 'Test string option', default="abc", 
-                          requires=[('int', 1, 'hide')])
-    
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
-    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
-                                           stroption, intoption])
-    return descr
-
-def test_hidden_if_in():
-    descr = make_description_requires()
-    cfg = Config(descr)
-    intoption = cfg.unwrap_from_path('int')
-    stroption = cfg.unwrap_from_path('str')
-    assert not stroption._is_hidden()
-    cfg.int = 1
-    raises(HiddenOptionError, "cfg.str")
-    raises(HiddenOptionError, 'cfg.str= "uvw"')
-    assert stroption._is_hidden()
-
-def test_hidden_if_in_with_group():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
-    gcdummy = BoolOption('dummy', 'dummy', default=False)
-
-    floatoption = FloatOption('float', 'Test float option', default=2.3)
-    
-    objspaceoption = ChoiceOption('objspace', 'Object space',
-                                ['std', 'thunk'], 'std')
-    booloption = BoolOption('bool', 'Test boolean option', default=True)
-    intoption = IntOption('int', 'Test int option', default=0)
-    stroption = StrOption('str', 'Test string option', default="abc")
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption], 
-                                                  requires=[('int', 1, 'hide')])
-    descr = OptionDescription('constraints', '', [gcgroup, booloption, 
-                                          objspaceoption, stroption, intoption])
-    cfg = Config(descr)
-    assert not gcgroup._is_hidden()
-    cfg.int = 1
-    raises(HiddenOptionError, "cfg.gc.name")
-#    raises(HiddenOptionError, 'cfg.gc= "uvw"')
-    assert gcgroup._is_hidden()
-
-def test_disabled_with_group():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
-    gcdummy = BoolOption('dummy', 'dummy', default=False)
-
-    floatoption = FloatOption('float', 'Test float option', default=2.3)
-    
-    objspaceoption = ChoiceOption('objspace', 'Object space',
-                                ['std', 'thunk'], 'std')
-    booloption = BoolOption('bool', 'Test boolean option', default=True)
-    intoption = IntOption('int', 'Test int option', default=0)
-    stroption = StrOption('str', 'Test string option', default="abc")
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption], 
-                                                  requires=[('int', 1, 'disable')])
-    descr = OptionDescription('constraints', '', [gcgroup, booloption, 
-                                          objspaceoption, stroption, intoption])
-    cfg = Config(descr)
-    assert not gcgroup._is_disabled()
-    cfg.int = 1
-    raises(DisabledOptionError, "cfg.gc.name")
-#    raises(HiddenOptionError, 'cfg.gc= "uvw"')
-    assert gcgroup._is_disabled()
-
-    
+from tiramisu.setting import owners, groups
+from tiramisu.config import Config
+from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\
+    BroadcastOption, SymLinkOption, OptionDescription, submulti
+from tiramisu.error import ConfigError, ValueWarning, PropertiesOptionError
+import warnings
+
+
+def test_consistency():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    a.impl_add_consistency('not_equal', b)
+    #consistency to itself
+    raises(ConfigError, "a.impl_add_consistency('not_equal', a)")
+
+
+def test_consistency_not_exists():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    a, b
+    raises(ConfigError, "a.impl_add_consistency('not_exists', b)")
+
+
+def test_consistency_unknown_params():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    a, b
+    raises(ValueError, "a.impl_add_consistency('not_equal', b, unknown=False)")
+
+
+def test_consistency_warnings_only_default():
+    a = IntOption('a', '', 1)
+    b = IntOption('b', '', 1)
+    warnings.simplefilter("always", ValueWarning)
+    with warnings.catch_warnings(record=True) as w:
+        a.impl_add_consistency('not_equal', b, warnings_only=True)
+    assert w != []
+
+
+def test_consistency_warnings_only():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    od = OptionDescription('od', '', [a, b])
+    a.impl_add_consistency('not_equal', b, warnings_only=True)
+    c = Config(od)
+    c.a = 1
+    warnings.simplefilter("always", ValueWarning)
+    with warnings.catch_warnings(record=True) as w:
+        c.b = 1
+    assert w != []
+
+
+def test_consistency_warnings_only_more_option():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    d = IntOption('d', '')
+    od = OptionDescription('od', '', [a, b, d])
+    a.impl_add_consistency('not_equal', b, d, warnings_only=True)
+    c = Config(od)
+    c.a = 1
+    warnings.simplefilter("always", ValueWarning)
+    with warnings.catch_warnings(record=True) as w:
+        c.b = 1
+    assert w != []
+    with warnings.catch_warnings(record=True) as w:
+        c.d = 1
+    assert w != []
+
+
+def test_consistency_not_equal():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    od = OptionDescription('od', '', [a, b])
+    a.impl_add_consistency('not_equal', b)
+    c = Config(od)
+    assert c.a is None
+    assert c.b is None
+    c.a = 1
+    del(c.a)
+    c.a = 1
+    raises(ValueError, "c.b = 1")
+    c.b = 2
+
+
+def test_consistency_not_equal_many_opts():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    c = IntOption('c', '')
+    d = IntOption('d', '')
+    e = IntOption('e', '')
+    f = IntOption('f', '')
+    od = OptionDescription('od', '', [a, b, c, d, e, f])
+    a.impl_add_consistency('not_equal', b, c, d, e, f)
+    c = Config(od)
+    assert c.a is None
+    assert c.b is None
+    #
+    c.a = 1
+    del(c.a)
+    #
+    c.a = 1
+    raises(ValueError, "c.b = 1")
+    #
+    c.b = 2
+    raises(ValueError, "c.f = 2")
+    raises(ValueError, "c.f = 1")
+    #
+    c.d = 3
+    raises(ValueError, "c.f = 3")
+    raises(ValueError, "c.a = 3")
+    raises(ValueError, "c.c = 3")
+    raises(ValueError, "c.e = 3")
+
+
+def test_consistency_not_in_config_1():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    a.impl_add_consistency('not_equal', b)
+    od1 = OptionDescription('od1', '', [a])
+    od = OptionDescription('root', '', [od1])
+    od
+    raises(ConfigError, "Config(od)")
+
+
+def test_consistency_not_in_config_2():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    a.impl_add_consistency('not_equal', b)
+    od1 = OptionDescription('od1', '', [a])
+    od2 = OptionDescription('od2', '', [b])
+    od = OptionDescription('root', '', [od1, od2])
+    Config(od)
+
+
+def test_consistency_not_in_config_3():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    a.impl_add_consistency('not_equal', b)
+    od1 = OptionDescription('od1', '', [a])
+    od2 = OptionDescription('od2', '', [b])
+    od = OptionDescription('root', '', [od1, od2])
+    od
+    #with subconfig
+    raises(ConfigError, "Config(od.od1)")
+
+
+def test_consistency_after_config():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    od1 = OptionDescription('od1', '', [a])
+    od2 = OptionDescription('od2', '', [b])
+    od = OptionDescription('root', '', [od1, od2])
+    Config(od)
+    raises(AttributeError, "a.impl_add_consistency('not_equal', b)")
+
+
+def test_consistency_not_equal_symlink():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    c = SymLinkOption('c', a)
+    od = OptionDescription('od', '', [a, b, c])
+    a.impl_add_consistency('not_equal', b)
+    c = Config(od)
+    assert set(od._cache_consistencies.keys()) == set([a, b])
+
+
+def test_consistency_not_equal_submulti():
+    a = IntOption('a', '', multi=submulti)
+    b = IntOption('b', '', multi=submulti)
+    od = OptionDescription('a', '', [a, b])
+    od.impl_set_group_type(groups.master)
+    raises(ConfigError, 'a.impl_add_consistency("not_equal", b)')
+
+
+def test_consistency_not_equal_default_submulti():
+    a = IntOption('a', '', [[1, 2]], multi=submulti)
+    b = IntOption('b', '', [[1]], multi=submulti)
+    od = OptionDescription('od', '', [a, b])
+    od
+    raises(ConfigError, "a.impl_add_consistency('not_equal', b)")
+
+
+def test_consistency_not_equal_multi():
+    a = IntOption('a', '', multi=True)
+    b = IntOption('b', '', multi=True)
+    od = OptionDescription('a', '', [a, b])
+    od.impl_set_group_type(groups.master)
+    a.impl_add_consistency('not_equal', b)
+    c = Config(od)
+    assert c.a == []
+    assert c.b == []
+    c.a = [1]
+    del(c.a)
+    c.a = [1]
+    raises(ValueError, "c.b = [1]")
+    c.b = [2]
+
+
+def test_consistency_not_equal_multi_default():
+    a = IntOption('a', '', multi=True)
+    b = IntOption('b', '', multi=True, default_multi=1)
+    od = OptionDescription('a', '', [a, b])
+    od.impl_set_group_type(groups.master)
+    a.impl_add_consistency('not_equal', b)
+    c = Config(od)
+    assert c.a == []
+    assert c.b == []
+    raises(ValueError, 'c.a = [1]')
+    c.a = [2]
+    del(c.a)
+
+
+def test_consistency_default():
+    a = IntOption('a', '', 1)
+    b = IntOption('b', '', 1)
+    a, b
+    raises(ValueError, "a.impl_add_consistency('not_equal', b)")
+
+
+def test_consistency_default_multi():
+    a = IntOption('a', '', [2, 1], multi=True)
+    b = IntOption('b', '', [1, 1], multi=True)
+    c = IntOption('c', '', [1, 2], multi=True)
+    b
+    raises(ValueError, "a.impl_add_consistency('not_equal', b)")
+    a.impl_add_consistency('not_equal', c)
+
+
+def test_consistency_default_diff():
+    a = IntOption('a', '', 3)
+    b = IntOption('b', '', 1)
+    od = OptionDescription('od', '', [a, b])
+    a.impl_add_consistency('not_equal', b)
+    c = Config(od)
+    raises(ValueError, "c.a = 1")
+    c.a = 2
+    c.b = 3
+    assert c.getowner(a) is owners.user
+    raises(ValueError, "del(c.a)")
+    assert c.getowner(a) is owners.user
+
+
+def test_consistency_ip_netmask():
+    a = IPOption('a', '')
+    b = NetmaskOption('b', '')
+    od = OptionDescription('od', '', [a, b])
+    b.impl_add_consistency('ip_netmask', a)
+    c = Config(od)
+    c.a = '192.168.1.1'
+    c.b = '255.255.255.0'
+    c.a = '192.168.1.2'
+    c.b = '255.255.255.255'
+    c.b = '255.255.255.0'
+    raises(ValueError, "c.a = '192.168.1.0'")
+    raises(ValueError, "c.a = '192.168.1.255'")
+
+
+def test_consistency_network_netmask():
+    a = NetworkOption('a', '')
+    b = NetmaskOption('b', '')
+    od = OptionDescription('od', '', [a, b])
+    b.impl_add_consistency('network_netmask', a)
+    c = Config(od)
+    c.a = '192.168.1.1'
+    c.b = '255.255.255.255'
+    del(c.b)
+    c.a = '192.168.1.0'
+    c.b = '255.255.255.0'
+    raises(ValueError, "c.a = '192.168.1.1'")
+
+
+def test_consistency_ip_in_network():
+    a = NetworkOption('a', '')
+    b = NetmaskOption('b', '')
+    c = IPOption('c', '')
+    d = IPOption('d', '')
+    od = OptionDescription('od', '', [a, b, c, d])
+    c.impl_add_consistency('in_network', a, b)
+    d.impl_add_consistency('in_network', a, b, warnings_only=True)
+    warnings.simplefilter("always", ValueWarning)
+    cfg = Config(od)
+    cfg.a = '192.168.1.0'
+    cfg.b = '255.255.255.0'
+    cfg.c = '192.168.1.1'
+    raises(ValueError, "cfg.c = '192.168.2.1'")
+    raises(ValueError, "cfg.c = '192.168.1.0'")
+    raises(ValueError, "cfg.c = '192.168.1.255'")
+    with warnings.catch_warnings(record=True) as w:
+        cfg.d = '192.168.2.1'
+    assert len(w) == 1
+
+
+def test_consistency_ip_in_network_len_error():
+    a = NetworkOption('a', '')
+    b = NetmaskOption('b', '')
+    c = IPOption('c', '')
+    od = OptionDescription('od', '', [a, b, c])
+    raises(ConfigError, "c.impl_add_consistency('in_network', a)")
+
+
+def test_consistency_ip_netmask_network_error():
+    a = IPOption('a', '')
+    b = NetworkOption('b', '')
+    c = NetmaskOption('c', '')
+    od = OptionDescription('od', '', [a, b, c])
+    c.impl_add_consistency('ip_netmask', a, b)
+    c = Config(od)
+    c.a = '192.168.1.1'
+    c.b = '192.168.1.0'
+    raises(ConfigError, "c.c = '255.255.255.0'")
+
+
+def test_consistency_ip_netmask_error_multi():
+    a = IPOption('a', '', multi=True)
+    b = NetmaskOption('b', '')
+    OptionDescription('od', '', [a, b])
+    raises(ConfigError, "b.impl_add_consistency('ip_netmask', a)")
+
+
+def test_consistency_ip_netmask_multi():
+    a = IPOption('a', '', multi=True)
+    b = NetmaskOption('b', '', multi=True)
+    od = OptionDescription('a', '', [a, b])
+    b.impl_add_consistency('ip_netmask', a)
+    od.impl_set_group_type(groups.master)
+    c = Config(od)
+    c.a = ['192.168.1.1']
+    c.b = ['255.255.255.0']
+    c.a = ['192.168.1.2']
+    c.b = ['255.255.255.255']
+    c.b = ['255.255.255.0']
+    raises(ValueError, "c.a = ['192.168.1.0']")
+
+
+def test_consistency_network_netmask_multi():
+    a = NetworkOption('a', '', multi=True)
+    b = NetmaskOption('b', '', multi=True)
+    od = OptionDescription('a', '', [a, b])
+    od.impl_set_group_type(groups.master)
+    b.impl_add_consistency('network_netmask', a)
+    c = Config(od)
+    c.a = ['192.168.1.1']
+    c.b = ['255.255.255.255']
+    del(c.b)
+    c.a = ['192.168.1.0']
+    c.b = ['255.255.255.0']
+    raises(ValueError, "c.a = ['192.168.1.1']")
+
+
+def test_consistency_network_netmask_multi_slave_default_multi():
+    a = NetworkOption('a', '', default_multi=u'192.168.1.0', multi=True, properties=('mandatory',))
+    b = NetmaskOption('b', '', default_multi=u'255.255.255.0', multi=True, properties=('mandatory',))
+    od = OptionDescription('a', '', [a, b])
+    od.impl_set_group_type(groups.master)
+    b.impl_add_consistency('network_netmask', a)
+    c = Config(od)
+    c.read_write()
+    c.a.append()
+
+
+def test_consistency_network_netmask_multi_slave_default():
+    a = NetworkOption('a', '', multi=True, properties=('mandatory',))
+    b = NetmaskOption('b', '', default_multi=u'255.255.255.0', multi=True, properties=('mandatory',))
+    od = OptionDescription('a', '', [a, b])
+    od.impl_set_group_type(groups.master)
+    b.impl_add_consistency('network_netmask', a)
+    c = Config(od)
+    c.read_write()
+    c.cfgimpl_get_settings().remove('cache')
+    assert c.a == []
+    assert c.b == []
+    c.a.append(u'192.168.1.0')
+    c.read_only()
+    assert c.a == [u'192.168.1.0']
+    assert c.b == [u'255.255.255.0']
+    c.read_write()
+    raises(ValueError, "c.a[0] = u'192.168.1.2'")
+    raises(ValueError, "c.a.append(u'192.168.1.1')")
+    raises(ValueError, "c.a = [u'192.168.1.0', u'192.168.1.1']")
+    c.a.append()
+    c.b = [u'255.255.255.0', u'255.255.255.255']
+    c.a = [u'192.168.1.0', u'192.168.1.1']
+
+
+def return_netmask(*args, **kwargs):
+    return u'255.255.255.0'
+
+
+def return_netmask2(master):
+    if master is not None:
+        if master.endswith('2.1'):
+            return u'255.255.255.0'
+        if not master.endswith('.0'):
+            return u'255.255.255.255'
+    return u'255.255.255.0'
+
+
+def test_consistency_network_netmask_multi_slave_callback():
+    a = NetworkOption('a', '', multi=True, properties=('mandatory',))
+    b = NetmaskOption('b', '', callback=return_netmask, multi=True, properties=('mandatory',))
+    od = OptionDescription('a', '', [a, b])
+    od.impl_set_group_type(groups.master)
+    b.impl_add_consistency('network_netmask', a)
+    c = Config(od)
+    c.read_write()
+    c.cfgimpl_get_settings().remove('cache')
+    assert c.a == []
+    assert c.b == []
+    c.a.append(u'192.168.1.0')
+    c.read_only()
+    assert c.a == [u'192.168.1.0']
+    assert c.b == [u'255.255.255.0']
+    c.read_write()
+    raises(ValueError, "c.a[0] = u'192.168.1.2'")
+    raises(ValueError, "c.a.append(u'192.168.1.1')")
+    raises(ValueError, "c.a = [u'192.168.1.0', u'192.168.1.1']")
+    c.a.append()
+    c.b = [u'255.255.255.0', u'255.255.255.255']
+    c.a = [u'192.168.1.0', u'192.168.1.1']
+
+
+def test_consistency_network_netmask_multi_slave_callback_value():
+    a = NetworkOption('a', '', multi=True, properties=('mandatory',))
+    b = NetmaskOption('b', '', callback=return_netmask2, callback_params={'': ((a, False),)}, multi=True, properties=('mandatory',))
+    od = OptionDescription('a', '', [a, b])
+    od.impl_set_group_type(groups.master)
+    b.impl_add_consistency('network_netmask', a)
+    c = Config(od)
+    c.read_write()
+    c.cfgimpl_get_settings().remove('cache')
+    assert c.a == []
+    assert c.b == []
+    c.a.append(u'192.168.1.0')
+    assert c.a == [u'192.168.1.0']
+    assert c.b == [u'255.255.255.0']
+    raises(ValueError, "c.a[0] = u'192.168.2.1'")
+    assert c.a == [u'192.168.1.0']
+    assert c.b == [u'255.255.255.0']
+    raises(ValueError, "c.a = [u'192.168.2.1']")
+    assert c.a == [u'192.168.1.0']
+    assert c.b == [u'255.255.255.0']
+    c.a.append(u'192.168.1.1')
+    c.a = [u'192.168.1.0', u'192.168.1.1']
+    c.b = [u'255.255.255.0', u'255.255.255.255']
+
+
+def test_consistency_ip_netmask_multi_master():
+    a = IPOption('a', '', multi=True)
+    b = NetmaskOption('b', '', multi=True)
+    od = OptionDescription('a', '', [a, b])
+    od.impl_set_group_type(groups.master)
+    b.impl_add_consistency('ip_netmask', a)
+    c = Config(od)
+    c.a = ['192.168.1.1']
+    c.b = ['255.255.255.0']
+    c.a = ['192.168.1.2']
+    c.b = ['255.255.255.255']
+    c.b = ['255.255.255.0']
+    raises(ValueError, "c.a = ['192.168.1.0']")
+    c.a = ['192.168.1.128']
+    raises(ValueError, "c.b = ['255.255.255.128']")
+    c.a = ['192.168.1.2', '192.168.1.3']
+
+
+def test_consistency_network_netmask_multi_master():
+    a = NetworkOption('a', '', multi=True)
+    b = NetmaskOption('b', '', multi=True)
+    od = OptionDescription('a', '', [a, b])
+    od.impl_set_group_type(groups.master)
+    b.impl_add_consistency('network_netmask', a)
+    c = Config(od)
+    c.a = ['192.168.1.1']
+    c.b = ['255.255.255.255']
+    del(c.b)
+    c.a = ['192.168.1.0']
+    c.b = ['255.255.255.0']
+    raises(ValueError, "c.a = ['192.168.1.1']")
+
+
+def test_consistency_broadcast():
+    a = NetworkOption('a', '', multi=True)
+    b = NetmaskOption('b', '', multi=True)
+    c = BroadcastOption('c', '', multi=True)
+    od = OptionDescription('a', '', [a, b, c])
+    od.impl_set_group_type(groups.master)
+    b.impl_add_consistency('network_netmask', a)
+    c.impl_add_consistency('broadcast', a, b)
+    c = Config(od)
+    #first, test network_netmask
+    c.a = ['192.168.1.128']
+    raises(ValueError, "c.b = ['255.255.255.0']")
+    #
+    c.a = ['192.168.1.0']
+    c.b = ['255.255.255.0']
+    c.c = ['192.168.1.255']
+    raises(ValueError, "c.a = ['192.168.1.1']")
+    #
+    c.a = ['192.168.1.0', '192.168.2.128']
+    c.b = ['255.255.255.0', '255.255.255.128']
+    c.c = ['192.168.1.255', '192.168.2.255']
+    raises(ValueError, "c.c[1] = '192.168.2.128'")
+    c.c[1] = '192.168.2.255'
+
+
+def test_consistency_broadcast_error():
+    a = NetworkOption('a', '', multi=True)
+    b = NetmaskOption('b', '', multi=True)
+    c = BroadcastOption('c', '', multi=True)
+    od = OptionDescription('a', '', [a, b, c])
+    od.impl_set_group_type(groups.master)
+    b.impl_add_consistency('network_netmask', a)
+    c.impl_add_consistency('broadcast', a)
+    c = Config(od)
+    raises(ConfigError, "c.a = ['192.168.1.0']")
+
+
+def test_consistency_broadcast_default_1():
+    a = NetworkOption('a', '', '192.168.1.0')
+    b = NetmaskOption('b', '', '255.255.255.128')
+    c = BroadcastOption('c', '', '192.168.2.127')
+    od = OptionDescription('a', '', [a, b, c])
+    od
+    raises(ValueError, "c.impl_add_consistency('broadcast', a, b)")
+
+
+def test_consistency_broadcast_default_2():
+    a = NetworkOption('a', '', '192.168.1.0')
+    b = NetmaskOption('b', '', '255.255.255.128')
+    d = BroadcastOption('d', '', '192.168.1.127')
+    od2 = OptionDescription('a', '', [a, b, d])
+    od2
+    d.impl_add_consistency('broadcast', a, b)
+
+
+def test_consistency_not_all():
+    #_cache_consistencies is not None by not options has consistencies
+    a = NetworkOption('a', '', multi=True)
+    b = NetmaskOption('b', '', multi=True)
+    c = BroadcastOption('c', '', multi=True)
+    od = OptionDescription('a', '', [a, b, c])
+    od.impl_set_group_type(groups.master)
+    b.impl_add_consistency('network_netmask', a)
+    c = Config(od)
+    c.a = ['192.168.1.0']
+    c.b = ['255.255.255.0']
+    c.c = ['192.168.1.255']
+
+
+def test_consistency_permissive():
+    a = IntOption('a', '', 1)
+    b = IntOption('b', '', 2, properties=('hidden',))
+    od = OptionDescription('od', '', [a, b])
+    a.impl_add_consistency('not_equal', b)
+    c = Config(od)
+    c.cfgimpl_get_settings().setpermissive(('hidden',))
+    c.read_write()
+    c.a = 1
+
+
+def test_consistency_disabled():
+    a = IntOption('a', '')
+    b = IntOption('b', '', properties=('disabled',))
+    od = OptionDescription('od', '', [a, b])
+    a.impl_add_consistency('not_equal', b)
+    c = Config(od)
+    c.read_write()
+    raises(PropertiesOptionError, "c.a = 1")
+
+
+def test_consistency_disabled_transitive():
+    a = IntOption('a', '')
+    b = IntOption('b', '', properties=('disabled',))
+    od = OptionDescription('od', '', [a, b])
+    a.impl_add_consistency('not_equal', b, transitive=False)
+    c = Config(od)
+    c.read_write()
+    c.a = 1
+
+
+def test_consistency_disabled_transitive_2():
+    a = IPOption('a', '')
+    b = IPOption('b', '')
+    c = NetworkOption('c', '', default='192.168.1.0')
+    d = NetmaskOption('d', '', default='255.255.255.0', properties=('disabled',))
+    od = OptionDescription('od', '', [a, b, c, d])
+    a.impl_add_consistency('not_equal', b)
+    a.impl_add_consistency('in_network', c, d, transitive=False)
+    c = Config(od)
+    c.read_write()
+    c.a = '192.168.1.1'
+    raises(ValueError, "c.b = '192.168.1.1'")
+    c.a = '192.168.2.1'
+    #
+    c.a = '192.168.1.1'
+    c.cfgimpl_get_settings().remove('disabled')
+    raises(ValueError, "c.a = '192.168.2.1'")
+
+
+def return_val(*args, **kwargs):
+    return '192.168.1.1'
+
+
+def test_consistency_with_callback():
+    a = NetworkOption('a', '', default='192.168.1.0')
+    b = NetmaskOption('b', '', default='255.255.255.0')
+    c = IPOption('c', '', callback=return_val, callback_params={'': ((a, False),)})
+    od = OptionDescription('od', '', [a, b, c])
+    c.impl_add_consistency('in_network', a, b)
+    cfg = Config(od)
+    cfg.c