"config.set() or config.setoption() or option.setoption()"
import autopath
from py.test import raises

from config import *
from option import *
from error 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)
    wantframework_option = BoolOption('wantframework', 'Test requires',
                                      default=False)    
    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
    descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
                                           wantref_option, stroption,
                                           wantframework_option,
                                           intoption, boolop])
    return descr
#____________________________________________________________
# change with __setattr__
def test_attribute_access():
    "Once set, option values can't be changed again by attribute access"
    s = StrOption("string", "", default="string")
    descr = OptionDescription("options", "", [s])
    config = Config(descr)
    # let's try to change it again
    config.string = "foo"
    assert config.string == "foo"

def test_setitem():
    s = StrOption("string", "", default=["string"], multi=True)
    descr = OptionDescription("options", "", [s])
    config = Config(descr)
    config.string = ["foo", "eggs"]
    

def test_reset():
    "if value is None, resets to default owner"
    s = StrOption("string", "", default="string")
    descr = OptionDescription("options", "", [s])
    config = Config(descr)
    config.string = "foo"
    assert config.string == "foo"
    assert config._cfgimpl_value_owners['string'] == 'user'
    config.string = None
    assert config.string == 'string'
    assert config._cfgimpl_value_owners['string'] == 'default'

def test_reset_with_multi():
    s = StrOption("string", "", default=["string"], default_multi="string" , multi=True)
    descr = OptionDescription("options", "", [s])
    config = Config(descr)
    config.string = []
    assert config.string == ["string"]
    assert config._cfgimpl_value_owners['string'] == ['default']
    config.string = ["eggs", "spam", "foo"]
    assert config._cfgimpl_value_owners['string'] == ['user', 'user', 'user']
    config.string = []
    assert config.string == ["string"]
    assert config._cfgimpl_value_owners['string'] == ['default']
    raises(ConfigError, "config.string = None")
    
def test_idontexist():
    descr = make_description()
    cfg = Config(descr)
    raises(AttributeError, "cfg.idontexist")
# ____________________________________________________________
def test_attribute_access_with_multi():
    s = StrOption("string", "", default=["string"], default_multi= "string" , multi=True)
    descr = OptionDescription("options", "", [s])
    config = Config(descr)
    config.string = ["foo", "bar"]
    assert config.string == ["foo", "bar"]

def test_item_access_with_multi():
    s = StrOption("string", "", default=["string"], multi=True)
    descr = OptionDescription("options", "", [s])
    config = Config(descr)
    config.string = ["foo", "bar"]
    assert config.string == ["foo", "bar"]
    assert config.string[0] == "foo"
    # FIXME
    config.string[0] = 'changetest'
#    assert config.string[0] == 'changetest'
#    assert config.string[

def test_access_with_multi_default():
    s = StrOption("string", "", default=["string"], multi=True)
    descr = OptionDescription("options", "", [s])
    config = Config(descr)
    assert config._cfgimpl_value_owners["string"] == ['default']
    config.string = ["foo", "bar"]
    assert config.string == ["foo", "bar"]
    assert config._cfgimpl_value_owners["string"] == ['user', 'user']

#def test_attribute_access_with_multi2():
#    s = StrOption("string", "", default="string", multi=True)
#    descr = OptionDescription("options", "", [s])
#    config = Config(descr)
#    config.string = ["foo", "bar"]
#    assert config.string == ["foo", "bar"]

def test_multi_with_requires():
    s = StrOption("string", "", default=["string"], default_multi="string", multi=True)
    intoption = IntOption('int', 'Test int option', default=0)
    stroption = StrOption('str', 'Test string option', default=["abc"], default_multi = "abc", 
                          requires=[('int', 1, 'hide')], multi=True)
    descr = OptionDescription("options", "", [s, intoption, stroption])
    config = Config(descr)
    assert stroption._is_hidden() == False
    config.int = 1
    raises(HiddenOptionError, "config.str = ['a', 'b']")
    assert stroption._is_hidden()

def test__requires_with_inverted():
    s = StrOption("string", "", default=["string"], multi=True)
    intoption = IntOption('int', 'Test int option', default=0)
    stroption = StrOption('str', 'Test string option', default=["abc"], default_multi = "abc",
                          requires=[('int', 1, 'hide', 'inverted')], multi=True)
    descr = OptionDescription("options", "", [s, intoption, stroption])
    config = Config(descr)
    assert stroption._is_hidden() == False
    config.int = 1
    assert stroption._is_hidden() == False

def test_multi_with_requires_in_another_group():
    s = StrOption("string", "", default=["string"], multi=True)
    intoption = IntOption('int', 'Test int option', default=0)
    descr = OptionDescription("options", "", [intoption])
    stroption = StrOption('str', 'Test string option', default=["abc"], 
                          requires=[('int', 1, 'hide')], multi=True)
    descr = OptionDescription("opt", "", [stroption])
    descr2 = OptionDescription("opt2", "", [intoption, s, descr])
    config = Config(descr2)
    assert stroption._is_hidden() == False
    config.int = 1
    raises(HiddenOptionError,  "config.opt.str = ['a', 'b']")
    assert stroption._is_hidden()

def test_apply_requires_from_config():
    s = StrOption("string", "", default=["string"], multi=True)
    intoption = IntOption('int', 'Test int option', default=0)
    descr = OptionDescription("options", "", [intoption])
    stroption = StrOption('str', 'Test string option', default=["abc"], 
                          requires=[('int', 1, 'hide')], multi=True)
    descr = OptionDescription("opt", "", [stroption])
    descr2 = OptionDescription("opt2", "", [intoption, s, descr])
    config = Config(descr2)
    assert stroption._is_hidden() == False
    config.int = 1
    try:
        config.opt.str
    except:
        pass 
    assert stroption._is_hidden()
    

def test_apply_requires_with_disabled():
    s = StrOption("string", "", default=["string"], multi=True)
    intoption = IntOption('int', 'Test int option', default=0)
    descr = OptionDescription("options", "", [intoption])
    stroption = StrOption('str', 'Test string option', default=["abc"], 
                          requires=[('int', 1, 'disable')], multi=True)
    descr = OptionDescription("opt", "", [stroption])
    descr2 = OptionDescription("opt2", "", [intoption, s, descr])
    config = Config(descr2)
    assert stroption._is_disabled() == False
    config.int = 1
    try:
        config.opt.str
    except:
        pass 
    assert stroption._is_disabled()

def test_multi_with_requires_with_disabled_in_another_group():
    s = StrOption("string", "", default=["string"], multi=True)
    intoption = IntOption('int', 'Test int option', default=0)
    descr = OptionDescription("options", "", [intoption])
    stroption = StrOption('str', 'Test string option', default=["abc"], 
                          requires=[('int', 1, 'disable')], multi=True)
    descr = OptionDescription("opt", "", [stroption])
    descr2 = OptionDescription("opt2", "", [intoption, s, descr])
    config = Config(descr2)
    assert stroption._is_disabled() == False
    config.int = 1
    raises(DisabledOptionError,  "config.opt.str = ['a', 'b']")
    assert stroption._is_disabled()

def test_multi_with_requires_that_is_multi():
    s = StrOption("string", "", default=["string"], multi=True)
    intoption = IntOption('int', 'Test int option', default=[0], multi=True)
    stroption = StrOption('str', 'Test string option', default=["abc"], 
                          requires=[('int', [1, 1], 'hide')], multi=True)
    descr = OptionDescription("options", "", [s, intoption, stroption])
    config = Config(descr)
    assert stroption._is_hidden() == False
    config.int = [1, 1]
    raises(HiddenOptionError, "config.str = ['a', 'b']")
    assert stroption._is_hidden()

def test_multi_with_bool():
    s = BoolOption("bool", "", default=[False], multi=True)
    descr = OptionDescription("options", "", [s])
    config = Config(descr)
    assert descr.bool.multi == True
    config.bool = [True, False]
    assert config._cfgimpl_values['bool'] == [True, False]
    assert config.bool == [True, False]

def test_multi_with_bool_two():
    s = BoolOption("bool", "", default=[False], multi=True)
    descr = OptionDescription("options", "", [s])
    config = Config(descr)
    assert descr.bool.multi == True
    raises(ConfigError, "config.bool = True")

def test_choice_access_with_multi():
    ch = ChoiceOption("t1", "", ["a", "b"], default=["a"], multi=True)
    descr = OptionDescription("options", "", [ch])
    config = Config(descr)
    config.t1 = ["a", "b", "a", "b"]
    assert config.t1 == ["a", "b", "a", "b"]
# ____________________________________________________________

def test_setoption_from_option():
    "a setoption directly from the option is **not** a good practice"
    booloption = BoolOption('bool', 'Test boolean option', default=True)
    descr = OptionDescription('descr', '', [booloption])
    cfg = Config(descr)
    booloption.setoption(cfg, False, 'owner')
    assert cfg.bool == False
# ____________________________________________________________
def test_set_mode_in_config():
    booloption = BoolOption('bool', 'Test boolean option', default=True, 
                                                                  mode='expert')
    descr = OptionDescription('descr', '', [booloption])
    cfg = Config(descr)
    cfg.cfgimpl_set_mode('expert')
    raises(ModeOptionError, "cfg.bool")
    cfg.cfgimpl_set_mode('normal')
    assert cfg.bool == True
#____________________________________________________________
def test_dwim_set():
    descr = OptionDescription("opt", "", [
        OptionDescription("sub", "", [
            BoolOption("b1", ""),
            ChoiceOption("c1", "", ['a', 'b', 'c'], 'a'),
            BoolOption("d1", ""),
        ]),
        BoolOption("b2", ""),
        BoolOption("d1", ""),
    ])
    c = Config(descr)
    c.set(b1=False, c1='b')
    assert not c.sub.b1
    assert c.sub.c1 == 'b'
    # new config, because you cannot change values once they are set
    c = Config(descr)
    c.set(b2=False, **{'sub.c1': 'c'})
    assert not c.b2
    assert c.sub.c1 == 'c'
    raises(AmbigousOptionError, "c.set(d1=True)")
    raises(NoMatchingOptionFound, "c.set(unknown='foo')")

def test_more_set():
    descr = OptionDescription("opt", "", [
        OptionDescription("s1", "", [
            BoolOption("a", "", default=False)]),
        IntOption("int", "", default=42)])
    d = {'s1.a': True, 'int': 23}
    config = Config(descr)
    config.set(**d)
    assert config.s1.a
    assert config.int == 23

def test_set_with_hidden_option():
    boolopt = BoolOption("a", "", default=False)
    boolopt.hide()
    descr = OptionDescription("opt", "", [
        OptionDescription("s1", "", [boolopt]),
        IntOption("int", "", default=42)])
    d = {'s1.a': True, 'int': 23}
    config = Config(descr)
    raises(HiddenOptionError, "config.set(**d)")

def test_set_with_unknown_option():
    boolopt = BoolOption("b", "", default=False)
    descr = OptionDescription("opt", "", [
        OptionDescription("s1", "", [boolopt]),
        IntOption("int", "", default=42)])
    d = {'s1.a': True, 'int': 23}
    config = Config(descr)
    raises(NoMatchingOptionFound, "config.set(**d)")


def test_set_symlink_option():
    boolopt = BoolOption("b", "", default=False)
    linkopt = SymLinkOption("c", "s1.b")
    descr = OptionDescription("opt", "", 
                              [linkopt, OptionDescription("s1", "", [boolopt])])
    config = Config(descr)
    setattr(config, "s1.b", True)
    setattr(config, "s1.b", False)
    assert config.s1.b == False
    assert config.c == False
    config.c = True
    assert config.s1.b == True
    assert config.c == True
    config.c = False
    assert config.s1.b == False
    assert config.c == False
    
#____________________________________________________________
def test_config_impl_values():
    descr = make_description()
    config = Config(descr, bool=False)
#    gcdummy.setoption(config, True, "user")
#    config.setoption("gc.dummy", True, "user")
    #config.gc.dummy = True
#    config.setoption("bool", False, "user")
    config.set(dummy=False)
    assert config.gc._cfgimpl_values == {'dummy': False, 'float': 2.3, 'name': 'ref'}
    ## acces to the option object 
#    config.gc._cfgimpl_descr.dummy.setoption(config, True, "user")
    assert config.gc.dummy == False
#    config.set(dummy=True)
#    assert config.gc.dummy == True

#____________________________________________________________
def test_accepts_multiple_changes_from_option():
    s = StrOption("string", "", default="string")
    descr = OptionDescription("options", "", [s])
    config = Config(descr)
    config.string = "egg"
    assert s.getdefault() == "string"
    assert config.string == "egg"
    s.setoption(config, 'blah', "default")
    assert s.getdefault() == "blah"
    assert config.string == "blah"
    s.setoption(config, 'bol', "user")
    assert config.string == 'bol'
    config.override({'string': "blurp"})
    assert config.string == 'blurp'
    assert s.getdefault() == 'blurp'

def test_allow_multiple_changes_from_config():
    """
    a `setoption` from the config object is much like the attribute access, 
    except the fact that value owner can bet set 
    """
    s = StrOption("string", "", default="string")
    s2 = StrOption("string2", "", default="string")
    suboption = OptionDescription("bip", "", [s2])
    descr = OptionDescription("options", "", [s, suboption])
    config = Config(descr)
    config.setoption("string", 'blah', "user")
    config.setoption("string", "oh", "user")    
    assert config.string == "oh"
    config.set(string2= 'blah')
    assert config.bip.string2 == 'blah'
# ____________________________________________________________

def test_overrides_are_defaults():
    descr = OptionDescription("test", "", [
        BoolOption("b1", "", default=False),
        BoolOption("b2", "", default=False),
        ])
    # overrides here
    config = Config(descr, b2=True)
    assert config.b2
    # test with a require
    config.b1 = True
    assert config.b2
 
 # ____________________________________________________________
 # accessing a value by the get method
def test_access_by_get():
    descr = make_description()
    cfg = Config(descr)
    raises(NotFoundError, "cfg.get('idontexist')" )
    assert cfg.get('wantref') == False
    assert cfg.gc.dummy == False
    assert cfg.get('dummy') == False

def test_access_by_get_whith_hide():
    b1 = BoolOption("b1", "")
    b1.hide()
    descr = OptionDescription("opt", "", [
    OptionDescription("sub", "", [
        b1,
        ChoiceOption("c1", "", ['a', 'b', 'c'], 'a'),
        BoolOption("d1", ""),
    ]),
    BoolOption("b2", ""),
    BoolOption("d1", ""),
    ])
    c = Config(descr)
    raises(HiddenOptionError, "c.get('b1')")