diff options
Diffstat (limited to 'test/test_configuration.py')
-rw-r--r-- | test/test_configuration.py | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/test/test_configuration.py b/test/test_configuration.py new file mode 100644 index 0000000..2dee7d0 --- /dev/null +++ b/test/test_configuration.py @@ -0,0 +1,507 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of logilab-common. +# +# logilab-common is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) any +# later version. +# +# logilab-common is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with logilab-common. If not, see <http://www.gnu.org/licenses/>. +import tempfile +import os +from os.path import join, dirname, abspath +import re + +from sys import version_info + +from logilab.common import attrdict +from logilab.common.compat import StringIO +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.optik_ext import OptionValueError +from logilab.common.configuration import Configuration, OptionError, \ + OptionsManagerMixIn, OptionsProviderMixIn, Method, read_old_config, \ + merge_options + +DATA = join(dirname(abspath(__file__)), 'data') + +OPTIONS = [('dothis', {'type':'yn', 'action': 'store', 'default': True, 'metavar': '<y or n>'}), + ('value', {'type': 'string', 'metavar': '<string>', 'short': 'v'}), + ('multiple', {'type': 'csv', 'default': ['yop', 'yep'], + 'metavar': '<comma separated values>', + 'help': 'you can also document the option'}), + ('number', {'type': 'int', 'default':2, 'metavar':'<int>', 'help': 'boom'}), + ('bytes', {'type': 'bytes', 'default':'1KB', 'metavar':'<bytes>'}), + ('choice', {'type': 'choice', 'default':'yo', 'choices': ('yo', 'ye'), + 'metavar':'<yo|ye>'}), + ('multiple-choice', {'type': 'multiple_choice', 'default':['yo', 'ye'], + 'choices': ('yo', 'ye', 'yu', 'yi', 'ya'), + 'metavar':'<yo|ye>'}), + ('named', {'type':'named', 'default':Method('get_named'), + 'metavar': '<key=val>'}), + + ('diffgroup', {'type':'string', 'default':'pouet', 'metavar': '<key=val>', + 'group': 'agroup'}), + ('reset-value', {'type': 'string', 'metavar': '<string>', 'short': 'r', + 'dest':'value'}), + + ('opt-b-1', {'type': 'string', 'metavar': '<string>', 'group': 'bgroup'}), + ('opt-b-2', {'type': 'string', 'metavar': '<string>', 'group': 'bgroup'}), + ] + +class MyConfiguration(Configuration): + """test configuration""" + def get_named(self): + return {'key': 'val'} + +class ConfigurationTC(TestCase): + + def setUp(self): + self.cfg = MyConfiguration(name='test', options=OPTIONS, usage='Just do it ! (tm)') + + def test_default(self): + cfg = self.cfg + self.assertEqual(cfg['dothis'], True) + self.assertEqual(cfg['value'], None) + self.assertEqual(cfg['multiple'], ['yop', 'yep']) + self.assertEqual(cfg['number'], 2) + self.assertEqual(cfg['bytes'], 1024) + self.assertIsInstance(cfg['bytes'], int) + self.assertEqual(cfg['choice'], 'yo') + self.assertEqual(cfg['multiple-choice'], ['yo', 'ye']) + self.assertEqual(cfg['named'], {'key': 'val'}) + + def test_base(self): + cfg = self.cfg + cfg.set_option('number', '0') + self.assertEqual(cfg['number'], 0) + self.assertRaises(OptionValueError, cfg.set_option, 'number', 'youpi') + self.assertRaises(OptionValueError, cfg.set_option, 'choice', 'youpi') + self.assertRaises(OptionValueError, cfg.set_option, 'multiple-choice', ('yo', 'y', 'ya')) + cfg.set_option('multiple-choice', 'yo, ya') + self.assertEqual(cfg['multiple-choice'], ['yo', 'ya']) + self.assertEqual(cfg.get('multiple-choice'), ['yo', 'ya']) + self.assertEqual(cfg.get('whatever'), None) + + def test_load_command_line_configuration(self): + cfg = self.cfg + args = cfg.load_command_line_configuration(['--choice', 'ye', '--number', '4', + '--multiple=1,2,3', '--dothis=n', + '--bytes=10KB', + 'other', 'arguments']) + self.assertEqual(args, ['other', 'arguments']) + self.assertEqual(cfg['dothis'], False) + self.assertEqual(cfg['multiple'], ['1', '2', '3']) + self.assertEqual(cfg['number'], 4) + self.assertEqual(cfg['bytes'], 10240) + self.assertEqual(cfg['choice'], 'ye') + self.assertEqual(cfg['value'], None) + args = cfg.load_command_line_configuration(['-v', 'duh']) + self.assertEqual(args, []) + self.assertEqual(cfg['value'], 'duh') + self.assertEqual(cfg['dothis'], False) + self.assertEqual(cfg['multiple'], ['1', '2', '3']) + self.assertEqual(cfg['number'], 4) + self.assertEqual(cfg['bytes'], 10240) + self.assertEqual(cfg['choice'], 'ye') + + def test_load_configuration(self): + cfg = self.cfg + args = cfg.load_configuration(choice='ye', number='4', + multiple='1,2,3', dothis='n', + multiple_choice=('yo', 'ya')) + self.assertEqual(cfg['dothis'], False) + self.assertEqual(cfg['multiple'], ['1', '2', '3']) + self.assertEqual(cfg['number'], 4) + self.assertEqual(cfg['choice'], 'ye') + self.assertEqual(cfg['value'], None) + self.assertEqual(cfg['multiple-choice'], ('yo', 'ya')) + + def test_load_configuration_file_case_insensitive(self): + file = tempfile.mktemp() + stream = open(file, 'w') + try: + stream.write("""[Test] + +dothis=no + +#value= + +# you can also document the option +multiple=yop,yepii + +# boom +number=3 + +bytes=1KB + +choice=yo + +multiple-choice=yo,ye + +named=key:val + + +[agroup] + +diffgroup=zou +""") + stream.close() + self.cfg.load_file_configuration(file) + self.assertEqual(self.cfg['dothis'], False) + self.assertEqual(self.cfg['value'], None) + self.assertEqual(self.cfg['multiple'], ['yop', 'yepii']) + self.assertEqual(self.cfg['diffgroup'], 'zou') + finally: + os.remove(file) + + def test_option_order(self): + """ Check that options are taken into account in the command line order + and not in the order they are defined in the Configuration object. + """ + file = tempfile.mktemp() + stream = open(file, 'w') + try: + stream.write("""[Test] +reset-value=toto +value=tata +""") + stream.close() + self.cfg.load_file_configuration(file) + finally: + os.remove(file) + self.assertEqual(self.cfg['value'], 'tata') + + def test_unsupported_options(self): + file = tempfile.mktemp() + stream = open(file, 'w') + try: + stream.write("""[Test] +whatever=toto +value=tata +""") + stream.close() + self.cfg.load_file_configuration(file) + finally: + os.remove(file) + self.assertEqual(self.cfg['value'], 'tata') + self.assertRaises(OptionError, self.cfg.__getitem__, 'whatever') + + def test_generate_config(self): + stream = StringIO() + self.cfg.generate_config(stream) + self.assertMultiLineEqual(stream.getvalue().strip(), """[TEST] + +dothis=yes + +#value= + +# you can also document the option +multiple=yop,yep + +# boom +number=2 + +bytes=1KB + +choice=yo + +multiple-choice=yo,ye + +named=key:val + +#reset-value= + + +[AGROUP] + +diffgroup=pouet + + +[BGROUP] + +#opt-b-1= + +#opt-b-2=""") + + def test_generate_config_with_space_string(self): + self.cfg['value'] = ' ' + stream = StringIO() + self.cfg.generate_config(stream) + self.assertMultiLineEqual(stream.getvalue().strip(), """[TEST] + +dothis=yes + +value=' ' + +# you can also document the option +multiple=yop,yep + +# boom +number=2 + +bytes=1KB + +choice=yo + +multiple-choice=yo,ye + +named=key:val + +reset-value=' ' + + +[AGROUP] + +diffgroup=pouet + + +[BGROUP] + +#opt-b-1= + +#opt-b-2=""") + + def test_generate_config_with_multiline_string(self): + self.cfg['value'] = 'line1\nline2\nline3' + stream = StringIO() + self.cfg.generate_config(stream) + self.assertMultiLineEqual(stream.getvalue().strip(), """[TEST] + +dothis=yes + +value= + line1 + line2 + line3 + +# you can also document the option +multiple=yop,yep + +# boom +number=2 + +bytes=1KB + +choice=yo + +multiple-choice=yo,ye + +named=key:val + +reset-value= + line1 + line2 + line3 + + +[AGROUP] + +diffgroup=pouet + + +[BGROUP] + +#opt-b-1= + +#opt-b-2=""") + + + def test_roundtrip(self): + cfg = self.cfg + f = tempfile.mktemp() + stream = open(f, 'w') + try: + self.cfg['dothis'] = False + self.cfg['multiple'] = ["toto", "tata"] + self.cfg['number'] = 3 + self.cfg['bytes'] = 2048 + cfg.generate_config(stream) + stream.close() + new_cfg = MyConfiguration(name='test', options=OPTIONS) + new_cfg.load_file_configuration(f) + self.assertEqual(cfg['dothis'], new_cfg['dothis']) + self.assertEqual(cfg['multiple'], new_cfg['multiple']) + self.assertEqual(cfg['number'], new_cfg['number']) + self.assertEqual(cfg['bytes'], new_cfg['bytes']) + self.assertEqual(cfg['choice'], new_cfg['choice']) + self.assertEqual(cfg['value'], new_cfg['value']) + self.assertEqual(cfg['multiple-choice'], new_cfg['multiple-choice']) + finally: + os.remove(f) + + def test_setitem(self): + self.assertRaises(OptionValueError, + self.cfg.__setitem__, 'multiple-choice', ('a', 'b')) + self.cfg['multiple-choice'] = ('yi', 'ya') + self.assertEqual(self.cfg['multiple-choice'], ('yi', 'ya')) + + def test_help(self): + self.cfg.add_help_section('bonus', 'a nice additional help') + help = self.cfg.help().strip() + # at least in python 2.4.2 the output is: + # ' -v <string>, --value=<string>' + # it is not unlikely some optik/optparse versions do print -v<string> + # so accept both + help = help.replace(' -v <string>, ', ' -v<string>, ') + help = re.sub('[ ]*(\r?\n)', '\\1', help) + USAGE = """Usage: Just do it ! (tm) + +Options: + -h, --help show this help message and exit + --dothis=<y or n> + -v<string>, --value=<string> + --multiple=<comma separated values> + you can also document the option [current: yop,yep] + --number=<int> boom [current: 2] + --bytes=<bytes> + --choice=<yo|ye> + --multiple-choice=<yo|ye> + --named=<key=val> + -r <string>, --reset-value=<string> + + Agroup: + --diffgroup=<key=val> + + Bgroup: + --opt-b-1=<string> + --opt-b-2=<string> + + Bonus: + a nice additional help""" + if version_info < (2, 5): + # 'usage' header is not capitalized in this version + USAGE = USAGE.replace('Usage: ', 'usage: ') + elif version_info < (2, 4): + USAGE = """usage: Just do it ! (tm) + +options: + -h, --help show this help message and exit + --dothis=<y or n> + -v<string>, --value=<string> + --multiple=<comma separated values> + you can also document the option + --number=<int> + --choice=<yo|ye> + --multiple-choice=<yo|ye> + --named=<key=val> + + Bonus: + a nice additional help +""" + self.assertMultiLineEqual(help, USAGE) + + + def test_manpage(self): + pkginfo = {} + with open(join(DATA, '__pkginfo__.py')) as fobj: + exec(fobj.read(), pkginfo) + self.cfg.generate_manpage(attrdict(pkginfo), stream=StringIO()) + + def test_rewrite_config(self): + changes = [('renamed', 'renamed', 'choice'), + ('moved', 'named', 'old', 'test'), + ] + read_old_config(self.cfg, changes, join(DATA, 'test.ini')) + stream = StringIO() + self.cfg.generate_config(stream) + self.assertMultiLineEqual(stream.getvalue().strip(), """[TEST] + +dothis=yes + +value=' ' + +# you can also document the option +multiple=yop + +# boom +number=2 + +bytes=1KB + +choice=yo + +multiple-choice=yo,ye + +named=key:val + +reset-value=' ' + + +[AGROUP] + +diffgroup=pouet + + +[BGROUP] + +#opt-b-1= + +#opt-b-2=""") + +class Linter(OptionsManagerMixIn, OptionsProviderMixIn): + options = ( + ('profile', {'type' : 'yn', 'metavar' : '<y_or_n>', + 'default': False, + 'help' : 'Profiled execution.'}), + ) + def __init__(self): + OptionsManagerMixIn.__init__(self, usage="") + OptionsProviderMixIn.__init__(self) + self.register_options_provider(self) + self.load_provider_defaults() + +class RegrTC(TestCase): + + def setUp(self): + self.linter = Linter() + + def test_load_defaults(self): + self.linter.load_command_line_configuration([]) + self.assertEqual(self.linter.config.profile, False) + + def test_register_options_multiple_groups(self): + """ensure multiple option groups can be registered at once""" + config = Configuration() + self.assertEqual(config.options, ()) + new_options = ( + ('option1', {'type': 'string', 'help': '', + 'group': 'g1', 'level': 2}), + ('option2', {'type': 'string', 'help': '', + 'group': 'g1', 'level': 2}), + ('option3', {'type': 'string', 'help': '', + 'group': 'g2', 'level': 2}), + ) + config.register_options(new_options) + self.assertEqual(config.options, new_options) + + +class MergeTC(TestCase): + + def test_merge1(self): + merged = merge_options([('dothis', {'type':'yn', 'action': 'store', 'default': True, 'metavar': '<y or n>'}), + ('dothis', {'type':'yn', 'action': 'store', 'default': False, 'metavar': '<y or n>'}), + ]) + self.assertEqual(len(merged), 1) + self.assertEqual(merged[0][0], 'dothis') + self.assertEqual(merged[0][1]['default'], True) + + def test_merge2(self): + merged = merge_options([('dothis', {'type':'yn', 'action': 'store', 'default': True, 'metavar': '<y or n>'}), + ('value', {'type': 'string', 'metavar': '<string>', 'short': 'v'}), + ('dothis', {'type':'yn', 'action': 'store', 'default': False, 'metavar': '<y or n>'}), + ]) + self.assertEqual(len(merged), 2) + self.assertEqual(merged[0][0], 'value') + self.assertEqual(merged[1][0], 'dothis') + self.assertEqual(merged[1][1]['default'], True) + +if __name__ == '__main__': + unittest_main() |