python 3.5 support
[tiramisu.git] / test / test_option_consistency.py
index a7ff41c..3761cf2 100644 (file)
@@ -1,22 +1,85 @@
-import autopath
+from .autopath import do_autopath
+do_autopath()
+
 from py.test import raises
 
 from tiramisu.setting import owners, groups
 from tiramisu.config import Config
 from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\
 from py.test import raises
 
 from tiramisu.setting import owners, groups
 from tiramisu.config import Config
 from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\
-    BroadcastOption, SymLinkOption, OptionDescription
-from tiramisu.error import ConfigError
+    BroadcastOption, StrOption, SymLinkOption, OptionDescription, submulti
+from tiramisu.error import ConfigError, ValueWarning, PropertiesOptionError
+import warnings
+
+
+def return_value(value=None):
+    return value
 
 
 def test_consistency():
     a = IntOption('a', '')
     b = IntOption('b', '')
 
 
 def test_consistency():
     a = IntOption('a', '')
     b = IntOption('b', '')
-    od = OptionDescription('od', '', [a, b])
     a.impl_add_consistency('not_equal', b)
     #consistency to itself
     raises(ConfigError, "a.impl_add_consistency('not_equal', a)")
     a.impl_add_consistency('not_equal', b)
     #consistency to itself
     raises(ConfigError, "a.impl_add_consistency('not_equal', a)")
-    #consistency with string
-    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 != []
+    assert len(w) == 1
+    with warnings.catch_warnings(record=True) as w:
+        c.d
+    assert w != []
+    assert len(w) == 1
+    with warnings.catch_warnings(record=True) as w:
+        c.d = 1
+    assert w != []
+    assert len(w) == 1
 
 
 def test_consistency_not_equal():
 
 
 def test_consistency_not_equal():
@@ -64,21 +127,71 @@ def test_consistency_not_equal_many_opts():
     raises(ValueError, "c.e = 3")
 
 
     raises(ValueError, "c.e = 3")
 
 
-def test_consistency_not_in_config():
+def test_consistency_not_equal_many_opts_one_disabled():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    c = IntOption('c', '')
+    d = IntOption('d', '')
+    e = IntOption('e', '')
+    f = IntOption('f', '')
+    g = IntOption('g', '', properties=('disabled',))
+    od = OptionDescription('od', '', [a, b, c, d, e, f, g])
+    a.impl_add_consistency('not_equal', b, c, d, e, f, g, transitive=False)
+    c = Config(od)
+    c.read_write()
+    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])
     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])
     od = OptionDescription('root', '', [od1])
+    od
     raises(ConfigError, "Config(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)
     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)")
 
 
     #with subconfig
     raises(ConfigError, "Config(od.od1)")
 
 
-def test_consistency_afer_config():
+def test_consistency_after_config():
     a = IntOption('a', '')
     b = IntOption('b', '')
     od1 = OptionDescription('od1', '', [a])
     a = IntOption('a', '')
     b = IntOption('b', '')
     od1 = OptionDescription('od1', '', [a])
@@ -98,10 +211,63 @@ def test_consistency_not_equal_symlink():
     assert set(od._cache_consistencies.keys()) == set([a, b])
 
 
     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_masterslave():
+    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]
+    del(c.a)
+    c.a.append(1)
+    c.make_dict()
+    c.b[0] = 3
+    c.a.append(2)
+    c.b[0] = 3
+    raises(ValueError, "c.b[1] = 3")
+
+
+def test_consistency_not_equal_masterslaves_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_not_equal_multi():
     a = IntOption('a', '', multi=True)
     b = IntOption('b', '', multi=True)
 def test_consistency_not_equal_multi():
     a = IntOption('a', '', multi=True)
     b = IntOption('b', '', multi=True)
-    od = OptionDescription('od', '', [a, b])
+    od = OptionDescription('a', '', [a, b])
     a.impl_add_consistency('not_equal', b)
     c = Config(od)
     assert c.a == []
     a.impl_add_consistency('not_equal', b)
     c = Config(od)
     assert c.a == []
@@ -111,11 +277,37 @@ def test_consistency_not_equal_multi():
     c.a = [1]
     raises(ValueError, "c.b = [1]")
     c.b = [2]
     c.a = [1]
     raises(ValueError, "c.b = [1]")
     c.b = [2]
+    raises(ValueError, "c.b = [2, 1]")
+    raises(ValueError, "c.b.append(1)")
+    c.b.append(3)
+    raises(ValueError, "c.b.append(3)")
+    raises(ValueError, "c.a.append(3)")
+    raises(ValueError, "c.a.append(3)")
+
+
+def test_consistency_not_equal_multi_default():
+    a = IntOption('a', '', multi=True, default=[1])
+    b = IntOption('b', '', multi=True, default=[1, 2])
+    od = OptionDescription('a', '', [a, b])
+    raises(ValueError, "a.impl_add_consistency('not_equal', b)")
+
+
+def test_consistency_not_equal_multi_default_modif():
+    a = IntOption('a', '', multi=True)
+    b = IntOption('b', '', multi=True, default=[1, 2])
+    od = OptionDescription('a', '', [a, b])
+    a.impl_add_consistency('not_equal', b)
+    c = Config(od)
+    assert c.a == []
+    assert c.b == [1, 2]
+    raises(ValueError, 'c.a.append(1)')
+    raises(ValueError, 'c.b.append(1)')
 
 
 def test_consistency_default():
     a = IntOption('a', '', 1)
     b = IntOption('b', '', 1)
 
 
 def test_consistency_default():
     a = IntOption('a', '', 1)
     b = IntOption('b', '', 1)
+    a, b
     raises(ValueError, "a.impl_add_consistency('not_equal', b)")
 
 
     raises(ValueError, "a.impl_add_consistency('not_equal', b)")
 
 
@@ -123,6 +315,7 @@ 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)
     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)
 
     raises(ValueError, "a.impl_add_consistency('not_equal', b)")
     a.impl_add_consistency('not_equal', c)
 
@@ -153,6 +346,7 @@ def test_consistency_ip_netmask():
     c.b = '255.255.255.255'
     c.b = '255.255.255.0'
     raises(ValueError, "c.a = '192.168.1.0'")
     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():
 
 
 def test_consistency_network_netmask():
@@ -169,18 +363,60 @@ def test_consistency_network_netmask():
     raises(ValueError, "c.a = '192.168.1.1'")
 
 
     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', '')
 def test_consistency_ip_netmask_error_multi():
     a = IPOption('a', '', multi=True)
     b = NetmaskOption('b', '')
-    od = OptionDescription('od', '', [a, 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)
     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('od', '', [a, b])
+    od = OptionDescription('a', '', [a, b])
     b.impl_add_consistency('ip_netmask', a)
     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 = Config(od)
     c.a = ['192.168.1.1']
     c.b = ['255.255.255.0']
@@ -193,7 +429,8 @@ def test_consistency_ip_netmask_multi():
 def test_consistency_network_netmask_multi():
     a = NetworkOption('a', '', multi=True)
     b = NetmaskOption('b', '', multi=True)
 def test_consistency_network_netmask_multi():
     a = NetworkOption('a', '', multi=True)
     b = NetmaskOption('b', '', multi=True)
-    od = OptionDescription('od', '', [a, b])
+    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']
     b.impl_add_consistency('network_netmask', a)
     c = Config(od)
     c.a = ['192.168.1.1']
@@ -204,6 +441,103 @@ def test_consistency_network_netmask_multi():
     raises(ValueError, "c.a = ['192.168.1.1']")
 
 
     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)
 def test_consistency_ip_netmask_multi_master():
     a = IPOption('a', '', multi=True)
     b = NetmaskOption('b', '', multi=True)
@@ -217,6 +551,8 @@ def test_consistency_ip_netmask_multi_master():
     c.b = ['255.255.255.255']
     c.b = ['255.255.255.0']
     raises(ValueError, "c.a = ['192.168.1.0']")
     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']
 
 
     c.a = ['192.168.1.2', '192.168.1.3']
 
 
@@ -260,14 +596,51 @@ def test_consistency_broadcast():
     c.c[1] = '192.168.2.255'
 
 
     c.c[1] = '192.168.2.255'
 
 
-def test_consistency_broadcast_default():
+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_warnings():
+    warnings.simplefilter("always", ValueWarning)
+    a = NetworkOption('a', '', properties=('mandatory', 'disabled'))
+    b = NetmaskOption('b', '', properties=('mandatory', 'disabled'))
+    c = NetmaskOption('c', '', properties=('mandatory', 'disabled'))
+    od = OptionDescription('a', '', [a, b, c])
+    b.impl_add_consistency('network_netmask', a, warnings_only=True)
+    c = Config(od)
+    with warnings.catch_warnings(record=True) as w:
+        c.a = '192.168.1.4'
+        c.b = '255.255.255.0'
+    assert len(w) == 1
+    c.read_write()
+    with warnings.catch_warnings(record=True) as w:
+        list(c.cfgimpl_get_values().mandatory_warnings())
+    assert len(w) == 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')
     a = NetworkOption('a', '', '192.168.1.0')
     b = NetmaskOption('b', '', '255.255.255.128')
     c = BroadcastOption('c', '', '192.168.2.127')
-    d = BroadcastOption('d', '', '192.168.1.127')
     od = OptionDescription('a', '', [a, b, c])
     od = OptionDescription('a', '', [a, b, c])
+    od
     raises(ValueError, "c.impl_add_consistency('broadcast', a, b)")
     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 = OptionDescription('a', '', [a, b, d])
+    od2
     d.impl_add_consistency('broadcast', a, b)
 
 
     d.impl_add_consistency('broadcast', a, b)
 
 
@@ -294,3 +667,108 @@ def test_consistency_permissive():
     c.cfgimpl_get_settings().setpermissive(('hidden',))
     c.read_write()
     c.a = 1
     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
+
+
+def test_consistency_double_warnings():
+    a = IntOption('a', '')
+    b = IntOption('b', '', 1)
+    c = IntOption('c', '', 1)
+    od = OptionDescription('od', '', [a, b, c])
+    warnings.simplefilter("always", ValueWarning)
+    a.impl_add_consistency('not_equal', b, warnings_only=True)
+    a.impl_add_consistency('not_equal', c, warnings_only=True)
+    cfg = Config(od)
+    with warnings.catch_warnings(record=True) as w:
+        cfg.a = 1
+    assert w != []
+    assert len(w) == 2
+    with warnings.catch_warnings(record=True) as w:
+        cfg.c = 2
+    assert w == []
+    with warnings.catch_warnings(record=True) as w:
+        cfg.a = 2
+    assert w != []
+    assert len(w) == 1
+    cfg.cfgimpl_get_settings().remove('warnings')
+    with warnings.catch_warnings(record=True) as w:
+        cfg.a = 1
+    assert w == []
+
+
+def test_consistency_warnings_error():
+    a = IntOption('a', '')
+    b = IntOption('b', '', 1)
+    c = IntOption('c', '', 1)
+    od = OptionDescription('od', '', [a, b, c])
+    warnings.simplefilter("always", ValueWarning)
+    a.impl_add_consistency('not_equal', b, warnings_only=True)
+    a.impl_add_consistency('not_equal', c)
+    cfg = Config(od)
+    with warnings.catch_warnings(record=True) as w:
+        raises(ValueError, "cfg.a = 1")
+    assert w == []
+
+
+def test_consistency_network_netmask_mandatory():
+    a = NetworkOption('a', '', multi=True, properties=('mandatory',), default=[u'0.0.0.0'])
+    b = NetmaskOption('b', '', multi=True, properties=('mandatory',), default_multi=u'0.0.0.0')
+    od = OptionDescription('a', '', [a, b])
+    od.impl_set_group_type(groups.master)
+    b.impl_add_consistency('network_netmask', a)
+    c = Config(od)
+    c.read_only()
+    c.cfgimpl_get_settings().remove('mandatory')
+    c.make_dict()