blob: 8dcfab19530f4986a6d02188609b2ecbf72fa29a [file] [log] [blame]
#!/usr/bin/python
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import copy
import StringIO
import sys
import unittest
from pprint import pprint
from wireless_automation.aspects import configurable
from wireless_automation.aspects import wireless_automation_logging
from wireless_automation.aspects.configurable import configobj
class ConfigurableTestClass1(configurable.Configurable):
"""
a test class with a configspec for testing, used below.
"""
CONFIGSPEC = configurable.list_to_configspec([
'#comment here',
'topnumber=integer(min=1,max=50,default=21)',
'[level_1]',
'#meaningful level_1 comment here.',
'midnumber=integer(min=1,max=50,default=22)',
'midnumber2=integer(min=1,max=50,default=23)',
'[[level_2]]',
'#meaningful level_2 comment here.',
'number=integer(min=1,max=50,default=24)',
'number1=integer(min=1,max=50,default=25)',
'number2=integer(min=1,max=50,default=26)',
'flavor=string(default=berrydefault)'
])
def __init__(self, input_config_data=None):
"""
@param input_config_data : the input config data
@return: a new object
"""
configurable.Configurable.__init__(self, input_config_data)
class BrokenConfigurableTestClass1(ConfigurableTestClass1):
"""
BrokenConfigurableTestClass1
"""
CONFIGSPEC = 'this should break the configspec reader'
class BrokenConfigurableTestClass1List(ConfigurableTestClass1):
"""
BrokenConfigurableTestClass1List
"""
CONFIGSPEC = ['this should break the configspec reader']
class ConfigurableTestClassNoConfigspec(configurable.Configurable):
"""
no configspec should raise an error
"""
def __init__(self, input_config_data=None):
configurable.Configurable.__init__(self, input_config_data)
class ConfigurableWithConfigspecDict(ConfigurableTestClass1):
"""
ConfigurableWithConfigspecDict
"""
CONFIGSPEC = {'should not work '}
class ConfigurableOnlyNeedsLevel1(ConfigurableTestClass1):
"""
Take a subsection of the config
"""
CONFIGSPEC = 'set this later '
class ConfigurableClassWithConfigobjAsConfigspec(configurable.Configurable):
"""
ConfigurableClassWithConfigobjAsConfigspec
"""
CONFIGSPEC = configobj.ConfigObj(configspec=['x:integer(default=11)'])
class ClassWithAllTheConfigTypes(configurable.Configurable):
CONFIGSPEC = [
"Number = integer(min=0, max=10, default=9)",
#"Float = float (min='0', max='1.111', default = '1.1')",
"Float = float(default=1)",
"TrueFalse = boolean(default=False)",
"IpAddress = ip_addr(default='10.0.0.1')",
"String = string(min=0,max=100, default='100 chars max')",
"Tuple = tuple(min=1, max=2, default=list('a','b'))",
"IntList = int_list(min=1, max=10, default =list(1,2))",
"FloatList = float_list(min=1, max=10, default =list('1.1',2))",
"BoolList = bool_list(min=1, max=10, "
"default =list(True,False,0,'yes',no))",
"IpAddrList = ip_addr_list(min=1, max=10, "
"default =list(10.10.10.1,1.1.1.1))",
"StringList = string_list(min=1, max=10, "
"default =list('string1', 'string2'))",
#Mixed list does not have the default keyword, making this
# not useful for Configurable, which uses defaults extensively.
#"MixedList = Do not use.
"AlwaysOK = pass(default = None) ",
"OptionList = option('red', 'blue', 'green',default='red' )",
]
def __init__(self, config):
super(ClassWithAllTheConfigTypes, self).__init__(config)
class TestConfigurableWithManyClasses(unittest.TestCase):
"""
How does reading a config file and splitting the sections work
"""
def test_modify_a_configspec(self):
"""
Modify a config spec in a derived class.
"""
class NewClass(ConfigurableTestClass1):
# ConfigObj keeps some deep links to the orig list used
# to construct it. Without the deepcopy, other tests that
# run after this test get the modified data and fail.
base_config = copy.deepcopy(ConfigurableTestClass1.CONFIGSPEC)
base_configspec = configurable.list_to_configspec(base_config)
base_configspec['level_1']['midnumber'] = 'float'
CONFIGSPEC = base_configspec
def __init__(self):
pass
x = NewClass()
assert x.CONFIGSPEC['level_1']['midnumber'] == 'float'
def test_config_obj_to_string(self):
"""
Can we get the string rep of a
"""
tt = ConfigurableTestClass1()
default_config = tt.get_default_config()
config_str = configurable.configobj_to_string(default_config)
print config_str
assert '#comment here' in config_str
assert 'flavor = berrydefault'in config_str
def test_add_section_to_configspec(self):
"""
Add a section to a config spec
"""
class1 = ConfigurableTestClass1
new_spec = configurable.nest_configspecs([
('NestedOneLevel', class1),
('NestedOneLevel', class1)])
# This should raise a duplicate section error
with self.assertRaises(configobj.ConfigspecError):
configobj.ConfigObj(configspec=new_spec)
assert new_spec[0] == '[NestedOneLevel]'
assert new_spec[4] == '[[level_1]]'
# We should have two of these now.
assert new_spec[18] == '[[level_1]]'
def test_combine_many_classes_configspec(self):
"""
Combine the CONFIGSPEC of many classes, maintaining
the [] hierarchy
"""
class ManyConfigSpecs(configurable.Configurable):
CONFIGSPEC = ['#TopTopComment '] + \
configurable.nest_configspecs([
('class1', ConfigurableTestClass1),
('class2', ConfigurableTestClass1)]
)
def __init__(self, config):
super(ManyConfigSpecs, self).__init__(config)
config = ManyConfigSpecs.get_default_config()
print "\n".join(config.configspec.write())
test_class = ManyConfigSpecs(config)
def test_write_and_read_config_file(self):
"""
test_write_and_read_config_file
"""
tt = ConfigurableTestClass1()
default_config = tt.get_default_config()
fh = StringIO.StringIO()
default_config.write(fh)
fh.seek(0)
read_config = configurable.read_config_from_file(fh)
self.assertEqual(read_config.write(),
default_config.write(),
'Reading the config file failed')
def test_pass_part_of_config_to_object(self):
"""
test_pass_part_of_config_to_object
"""
tt = ConfigurableTestClass1()
sub_configspec = configurable.get_section_of_configspec(
tt.config.configspec, 'level_1')
ConfigurableOnlyNeedsLevel1.CONFIGSPEC = sub_configspec
config = tt.get_default_config()
section = configurable.get_section_of_config(config, 'level_1')
tt1 = ConfigurableOnlyNeedsLevel1(section)
def test_get_section_of_configspec_should_raise_on_dict_input(self):
"""
test_get_section_of_configspec_should_raise_on_dict_input
"""
tt = ConfigurableTestClass1()
with self.assertRaises(configurable.ConfigurationError):
sub_configspec = configurable.get_section_of_configspec(
{'spec': '=integer'}, {'level_1'})
def test_build_config_from_two_classes_and_get_subsections(self):
"""
test_build_config_from_two_classes_and_get_subsections
"""
tt = ConfigurableTestClass1()
combined = configurable.combine_configs([
('first', tt.config),
('second', tt.config)
])
combined_list_str = combined.write()
self.assertEqual(combined_list_str[0], '[first]', 'combined lists')
self.assertEqual(combined['first'], tt.config, '[first] missing')
self.assertEqual(combined['second'], tt.config, '[second] missing')
def test_combine_configs_should_raise_on_duplicate_names(self):
"""
test_combine_configs_should_raise_on_duplicate_names
"""
tt = ConfigurableTestClass1()
with self.assertRaises(configurable.ConfigurationError):
combined = configurable.combine_configs([
('first', tt.config),
('first', tt.config)
])
def test_get_section_of_configspec_should_raise_on_missing_section(self):
"""
test_get_section_of_configspec_should_raise_on_missing_section
"""
configspec = ConfigurableTestClass1.CONFIGSPEC
section = 'non_existant_section_name'
with self.assertRaises(configurable.ConfigurationError):
configspec_section = configurable.get_section_of_configspec(
configspec, section)
def test_get_section_of_configspec(self):
"""
test_get_section_of_configspec
"""
configspec = ConfigurableTestClass1.CONFIGSPEC
section = 'level_1'
configspec_section = configurable.get_section_of_configspec(
configspec, section)
self.assertEqual(
configspec_section[0],
'midnumber = integer(min=1,max=50,default=22)',
'get_section of configspec failed')
def test_get_section_of_config(self):
"""
test_get_section_of_config
"""
tt = ConfigurableTestClass1()
config = tt.get_default_config()
sub_section = configurable.get_section_of_config(config, 'level_1')
self.assertEqual(sub_section[0], 'midnumber = 22')
class TestConfigurable(unittest.TestCase):
"""
Make a derived class of configurable
"""
def test_make_config_with_many_types_works(self):
"""
test_make_config_with_many_types_works
"""
config = ClassWithAllTheConfigTypes.get_default_config()
a = ClassWithAllTheConfigTypes(config)
def test_configspec_creates_valid_default_config(self):
"""
test_configspec_creates_valid_default_config
"""
tt = ConfigurableTestClass1()
default_config = tt.get_default_config()
self.assertTrue(tt.is_this_config_valid(default_config))
def add_bad_data_and_check(key, value):
"""
add_bad_data_and_check
@param value: the value
@param key: the key
"""
bad_config = copy.copy(default_config)
bad_config[key] = value
self.assertFalse(tt.is_this_config_valid(bad_config))
add_bad_data_and_check('topnumber', '111') # Too large
add_bad_data_and_check('flavors', 'not_in_spec')
add_bad_data_and_check('number', 'string_not_int')
def test_many_different_invalid_config_parts(self):
"""
test_many_different_invalid_config_parts
"""
tt = ConfigurableTestClass1()
default_config = tt.get_default_config()
bad_config = copy.copy(default_config)
bad_config['level_1']['not_there'] = 1212
self.assertFalse(tt.is_this_config_valid(bad_config))
bad_config = copy.copy(default_config)
bad_config['level_1']['midnumber'] = 1212
self.assertFalse(tt.is_this_config_valid(bad_config))
bad_config = copy.copy(default_config)
bad_config['level_1']['midnumber2'] = 121232
self.assertFalse(tt.is_this_config_valid(bad_config))
bad_config = copy.copy(default_config)
bad_config['level_1']['level_22'] = 121232
self.assertFalse(tt.is_this_config_valid(bad_config))
bad_config = copy.copy(default_config)
bad_config['level_1']['level_2']['number2'] = 121232
self.assertFalse(tt.is_this_config_valid(bad_config))
def test_many_invalid_config_parts_at_once(self):
"""
test_many_invalid_config_parts_at_once
"""
tt = ConfigurableTestClass1()
default_config = tt.get_default_config()
bad_config = copy.copy(default_config)
bad_config['level_1']['not_there'] = 1212
bad_config['level_1']['midnumber'] = 1212
bad_config['level_1']['midnumber2'] = 121232
bad_config['level_1']['level_22'] = 121232
bad_config['level_1']['level_2']['number2'] = 121232
self.assertFalse(tt.is_this_config_valid(bad_config))
def test_is_this_config_valid_should_raise_on_bad_input(self):
with self.assertRaises(configurable.ConfigurationError):
ConfigurableTestClass1.is_this_config_valid(['bad_type'])
def test_missing_configspec_raises_error(self):
"""
test_missing_configspec_raises_error
"""
with self.assertRaises(configurable.ConfigurationError):
tt = ConfigurableTestClassNoConfigspec()
def test_configspec_must_not_be_dict(self):
"""
test_configspec_must_not_be_dict
"""
with self.assertRaises(configurable.ConfigurationError):
tt = ConfigurableWithConfigspecDict()
def test_subset_of_config_options_add_to_defaults(self):
"""
test_subset_of_config_options_add_to_defaults
"""
tt = ConfigurableTestClass1(['topnumber=33'])
self.assertEqual(tt.config['topnumber'], 33)
self.assertEqual(tt.config['level_1']['midnumber'], 22)
tt.is_this_config_valid(tt.config)
def test_get_items_in_config_not_in_configspec(self):
"""
test_get_items_in_config_not_in_configspec
"""
config = ConfigurableTestClass1.get_default_config()
config['extra_junk'] = 33
extras = ConfigurableTestClass1.\
_get_items_in_config_not_in_configspec(config)
self.assertEqual(extras[0][1], 'extra_junk')
def test_invalid_config_list_raises_error(self):
"""
test_extra_values_raise_error
"""
with self.assertRaises(configurable.ConfigurationError):
tt = ConfigurableTestClass1(['extra_thing=33'])
def test_invalid_config_dict_raises_error(self):
"""
test_invalid_config_raises_error
"""
with self.assertRaises(configurable.ConfigurationError):
tt = ConfigurableTestClass1({'bad_config_data': 333})
def test_init_with_configobj_as_confgspec(self):
"""
test_init_with_configobj_as_confgspec
"""
tt = ConfigurableClassWithConfigobjAsConfigspec()
def test_valid_config(self):
"""
test_valid_config
"""
tt = ConfigurableTestClass1()
default_config = tt.get_default_config()
tt2 = ConfigurableTestClass1(default_config)
def test_check_config_raises_error(self):
"""
test_get_default_config_raises_error
"""
with self.assertRaises(configurable.ConfigurationError):
tt = BrokenConfigurableTestClass1()
def test_get_default_config_raises_error(self):
"""
test_get_default_config_raises_error
"""
with self.assertRaises(configurable.ConfigurationError):
tt = BrokenConfigurableTestClass1List.get_default_config()
if __name__ == '__main__':
log = wireless_automation_logging.setup_logging(
'configurable', output_stream=sys.stdout)
unittest.main(verbosity=2)