summaryrefslogtreecommitdiff
path: root/test/test_configuration.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/test_configuration.py')
-rw-r--r--test/test_configuration.py507
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()