summaryrefslogtreecommitdiffstats
path: root/testsuite
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2014-09-16 15:50:04 -0700
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2014-11-10 17:35:43 -0600
commit477c9c4119df5fd45c1129651922d238dccad8c9 (patch)
tree8ccbb610e1748a4cb07b93686d6926e4521db815 /testsuite
parent389ce1a86b704222ddc9458cd49c281e7601b803 (diff)
downloadbcfg2-477c9c4119df5fd45c1129651922d238dccad8c9.tar.gz
bcfg2-477c9c4119df5fd45c1129651922d238dccad8c9.tar.bz2
bcfg2-477c9c4119df5fd45c1129651922d238dccad8c9.zip
testsuite: Added unit tests for new option parsing
Diffstat (limited to 'testsuite')
-rw-r--r--testsuite/Testsrc/Testlib/TestClient/TestTools/Test_init.py19
-rw-r--r--testsuite/Testsrc/Testlib/TestOptions/One.py6
-rw-r--r--testsuite/Testsrc/Testlib/TestOptions/TestComponents.py210
-rw-r--r--testsuite/Testsrc/Testlib/TestOptions/TestConfigFiles.py48
-rw-r--r--testsuite/Testsrc/Testlib/TestOptions/TestOptionGroups.py145
-rw-r--r--testsuite/Testsrc/Testlib/TestOptions/TestOptions.py469
-rw-r--r--testsuite/Testsrc/Testlib/TestOptions/TestSubcommands.py141
-rw-r--r--testsuite/Testsrc/Testlib/TestOptions/TestTypes.py111
-rw-r--r--testsuite/Testsrc/Testlib/TestOptions/TestWildcards.py47
-rw-r--r--testsuite/Testsrc/Testlib/TestOptions/Two.py6
-rw-r--r--testsuite/Testsrc/Testlib/TestOptions/__init__.py85
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testbase.py1
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testhelpers.py1
-rw-r--r--testsuite/Testsrc/Testlib/TestUtils.py4
-rw-r--r--testsuite/Testsrc/__init__.py0
15 files changed, 1279 insertions, 14 deletions
diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/Test_init.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/Test_init.py
index 0e9e3a141..b9de703ff 100644
--- a/testsuite/Testsrc/Testlib/TestClient/TestTools/Test_init.py
+++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/Test_init.py
@@ -17,18 +17,17 @@ while path != "/":
path = os.path.dirname(path)
from common import *
-# try to find true
-if os.path.exists("/bin/true"):
- TRUE = "/bin/true"
-elif os.path.exists("/usr/bin/true"):
- TRUE = "/usr/bin/true"
-else:
- TRUE = None
-
class TestTool(Bcfg2TestCase):
test_obj = Tool
+ if os.path.exists("/bin/true"):
+ true = "/bin/true"
+ elif os.path.exists("/usr/bin/true"):
+ true = "/usr/bin/true"
+ else:
+ true = None
+
def setUp(self):
set_setup_default('command_timeout')
set_setup_default('interactive', False)
@@ -77,11 +76,11 @@ class TestTool(Bcfg2TestCase):
["/test"] + [e.get("name") for e in important])
t.getSupportedEntries.assert_called_with()
- @skipIf(TRUE is None, "/bin/true or equivalent not found")
+ @skipIf(true is None, "/bin/true or equivalent not found")
def test__check_execs(self):
t = self.get_obj()
if t.__execs__ == []:
- t.__execs__.append(TRUE)
+ t.__execs__.append(self.true)
@patch("os.stat")
def inner(mock_stat):
diff --git a/testsuite/Testsrc/Testlib/TestOptions/One.py b/testsuite/Testsrc/Testlib/TestOptions/One.py
new file mode 100644
index 000000000..dac7f4558
--- /dev/null
+++ b/testsuite/Testsrc/Testlib/TestOptions/One.py
@@ -0,0 +1,6 @@
+"""Test module for component loading."""
+
+
+class One(object):
+ """Test class for component loading."""
+ pass
diff --git a/testsuite/Testsrc/Testlib/TestOptions/TestComponents.py b/testsuite/Testsrc/Testlib/TestOptions/TestComponents.py
new file mode 100644
index 000000000..d637d34c5
--- /dev/null
+++ b/testsuite/Testsrc/Testlib/TestOptions/TestComponents.py
@@ -0,0 +1,210 @@
+"""test component loading."""
+
+import argparse
+import os
+
+from Bcfg2.Options import Option, BooleanOption, ComponentAction, get_parser, \
+ new_parser, Types, ConfigFileAction
+
+from testsuite.Testsrc.Testlib.TestOptions import make_config, One, Two, \
+ OptionTestCase
+
+
+# create a bunch of fake components for testing component loading options
+
+class ChildOne(object):
+ """fake component for testing component loading."""
+ options = [Option("--child-one")]
+
+
+class ChildTwo(object):
+ """fake component for testing component loading."""
+ options = [Option("--child-two")]
+
+
+class ChildComponentAction(ComponentAction):
+ """child component loader action."""
+ islist = False
+ mapping = {"one": ChildOne,
+ "two": ChildTwo}
+
+
+class ComponentOne(object):
+ """fake component for testing component loading."""
+ options = [BooleanOption("--one")]
+
+
+class ComponentTwo(object):
+ """fake component for testing component loading."""
+ options = [Option("--child", default="one", action=ChildComponentAction)]
+
+
+class ComponentThree(object):
+ """fake component for testing component loading."""
+ options = [BooleanOption("--three")]
+
+
+class ConfigFileComponent(object):
+ """fake component for testing component loading."""
+ options = [Option("--config2", action=ConfigFileAction),
+ Option(cf=("config", "test"), dest="config2_test",
+ default="bar")]
+
+
+class ParentComponentAction(ComponentAction):
+ """parent component loader action."""
+ mapping = {"one": ComponentOne,
+ "two": ComponentTwo,
+ "three": ComponentThree,
+ "config": ConfigFileComponent}
+
+
+class TestComponentOptions(OptionTestCase):
+ """test cases for component loading."""
+
+ def setUp(self):
+ self.options = [
+ Option("--parent", type=Types.comma_list,
+ default=["one", "two"], action=ParentComponentAction)]
+
+ self.result = argparse.Namespace()
+ new_parser()
+ self.parser = get_parser(components=[self], namespace=self.result,
+ description="component testing parser")
+
+ @make_config()
+ def test_loading_components(self, config_file):
+ """load a single component during option parsing."""
+ self.parser.parse(["-C", config_file, "--parent", "one"])
+ self.assertEqual(self.result.parent, [ComponentOne])
+
+ @make_config()
+ def test_component_option(self, config_file):
+ """use options from a component loaded during option parsing."""
+ self.parser.parse(["--one", "-C", config_file, "--parent", "one"])
+ self.assertEqual(self.result.parent, [ComponentOne])
+ self.assertTrue(self.result.one)
+
+ @make_config()
+ def test_multi_component_load(self, config_file):
+ """load multiple components during option parsing."""
+ self.parser.parse(["-C", config_file, "--parent", "one,three"])
+ self.assertEqual(self.result.parent, [ComponentOne, ComponentThree])
+
+ @make_config()
+ def test_multi_component_options(self, config_file):
+ """use options from multiple components during option parsing."""
+ self.parser.parse(["-C", config_file, "--three",
+ "--parent", "one,three", "--one"])
+ self.assertEqual(self.result.parent, [ComponentOne, ComponentThree])
+ self.assertTrue(self.result.one)
+ self.assertTrue(self.result.three)
+
+ @make_config()
+ def test_component_default_not_loaded(self, config_file):
+ """options from default but unused components not available."""
+ self.assertRaises(
+ SystemExit,
+ self.parser.parse,
+ ["-C", config_file, "--child", "one", "--parent", "one"])
+
+ @make_config()
+ def test_tiered_components(self, config_file):
+ """load child component."""
+ self.parser.parse(["-C", config_file, "--parent", "two",
+ "--child", "one"])
+ self.assertEqual(self.result.parent, [ComponentTwo])
+ self.assertEqual(self.result.child, ChildOne)
+
+ @make_config()
+ def test_options_tiered_components(self, config_file):
+ """use options from child component."""
+ self.parser.parse(["--child-one", "foo", "-C", config_file, "--parent",
+ "two", "--child", "one"])
+ self.assertEqual(self.result.parent, [ComponentTwo])
+ self.assertEqual(self.result.child, ChildOne)
+ self.assertEqual(self.result.child_one, "foo")
+
+ @make_config()
+ def test_bogus_component(self, config_file):
+ """error out with bad component name."""
+ self.assertRaises(SystemExit,
+ self.parser.parse,
+ ["-C", config_file, "--parent", "blargle"])
+
+ @make_config()
+ @make_config({"config": {"test": "foo"}})
+ def test_config_component(self, config1, config2):
+ """load component with alternative config file."""
+ self.parser.parse(["-C", config1, "--config2", config2,
+ "--parent", "config"])
+ self.assertEqual(self.result.config2, config2)
+ self.assertEqual(self.result.config2_test, "foo")
+
+ @make_config()
+ def test_config_component_no_file(self, config_file):
+ """load component with missing alternative config file."""
+ self.parser.parse(["-C", config_file, "--parent", "config"])
+ self.assertEqual(self.result.config2, None)
+
+
+class ImportComponentAction(ComponentAction):
+ """action that imports real classes for testing."""
+ islist = False
+ bases = ["testsuite.Testsrc.Testlib.TestOptions"]
+
+
+class ImportModuleAction(ImportComponentAction):
+ """action that only imports modules for testing."""
+ module = True
+
+
+class TestImportComponentOptions(OptionTestCase):
+ """test cases for component loading."""
+
+ def setUp(self):
+ self.options = [Option("--cls", action=ImportComponentAction),
+ Option("--module", action=ImportModuleAction)]
+
+ self.result = argparse.Namespace()
+ new_parser()
+ self.parser = get_parser(components=[self], namespace=self.result)
+
+ @make_config()
+ def test_import_component(self, config_file):
+ """load class components by importing."""
+ self.parser.parse(["-C", config_file, "--cls", "One"])
+ self.assertEqual(self.result.cls, One.One)
+
+ @make_config()
+ def test_import_module(self, config_file):
+ """load module components by importing."""
+ self.parser.parse(["-C", config_file, "--module", "One"])
+ self.assertEqual(self.result.module, One)
+
+ @make_config()
+ def test_import_full_path(self, config_file):
+ """load components by importing the full path."""
+ self.parser.parse(["-C", config_file, "--cls", "os.path"])
+ self.assertEqual(self.result.cls, os.path)
+
+ @make_config()
+ def test_import_bogus_class(self, config_file):
+ """fail to load class component that cannot be imported."""
+ self.assertRaises(SystemExit,
+ self.parser.parse,
+ ["-C", config_file, "--cls", "Three"])
+
+ @make_config()
+ def test_import_bogus_module(self, config_file):
+ """fail to load module component that cannot be imported."""
+ self.assertRaises(SystemExit,
+ self.parser.parse,
+ ["-C", config_file, "--module", "Three"])
+
+ @make_config()
+ def test_import_bogus_path(self, config_file):
+ """fail to load component that cannot be imported by full path."""
+ self.assertRaises(SystemExit,
+ self.parser.parse,
+ ["-C", config_file, "--cls", "Bcfg2.No.Such.Thing"])
diff --git a/testsuite/Testsrc/Testlib/TestOptions/TestConfigFiles.py b/testsuite/Testsrc/Testlib/TestOptions/TestConfigFiles.py
new file mode 100644
index 000000000..aee2ff666
--- /dev/null
+++ b/testsuite/Testsrc/Testlib/TestOptions/TestConfigFiles.py
@@ -0,0 +1,48 @@
+"""test reading multiple config files."""
+
+import argparse
+
+from Bcfg2.Options import Option, PathOption, ConfigFileAction, get_parser, \
+ new_parser
+
+from testsuite.Testsrc.Testlib.TestOptions import make_config, OptionTestCase
+
+
+class TestConfigFiles(OptionTestCase):
+ def setUp(self):
+ self.options = [
+ PathOption(cf=("test", "config2"), action=ConfigFileAction),
+ PathOption(cf=("test", "config3"), action=ConfigFileAction),
+ Option(cf=("test", "foo")),
+ Option(cf=("test", "bar")),
+ Option(cf=("test", "baz"))]
+ self.results = argparse.Namespace()
+ new_parser()
+ self.parser = get_parser(components=[self], namespace=self.results)
+
+ @make_config({"test": {"baz": "baz"}})
+ def test_config_files(self, config3):
+ """read multiple config files."""
+ # Because make_config() generates temporary files for the
+ # configuration, we have to work backwards here. first we
+ # generate config3, then we generate config2 (which includes a
+ # reference to config3), then we finally generate the main
+ # config file, which contains a reference to config2. oh how
+ # I wish we could use context managers here...
+
+ @make_config({"test": {"bar": "bar", "config3": config3}})
+ def inner1(config2):
+ @make_config({"test": {"foo": "foo", "config2": config2}})
+ def inner2(config):
+ self.parser.parse(["-C", config])
+ self.assertEqual(self.results.foo, "foo")
+ self.assertEqual(self.results.bar, "bar")
+ self.assertEqual(self.results.baz, "baz")
+
+ inner2()
+
+ inner1()
+
+ def test_no_config_file(self):
+ """fail to read config file."""
+ self.assertRaises(SystemExit, self.parser.parse, [])
diff --git a/testsuite/Testsrc/Testlib/TestOptions/TestOptionGroups.py b/testsuite/Testsrc/Testlib/TestOptions/TestOptionGroups.py
new file mode 100644
index 000000000..de1abbb1b
--- /dev/null
+++ b/testsuite/Testsrc/Testlib/TestOptions/TestOptionGroups.py
@@ -0,0 +1,145 @@
+"""test reading multiple config files."""
+
+import argparse
+
+from Bcfg2.Options import Option, BooleanOption, Parser, OptionGroup, \
+ ExclusiveOptionGroup, WildcardSectionGroup, new_parser, get_parser
+
+from testsuite.common import Bcfg2TestCase
+from testsuite.Testsrc.Testlib.TestOptions import make_config, OptionTestCase
+
+
+class TestOptionGroups(Bcfg2TestCase):
+ def setUp(self):
+ self.options = None
+
+ def _test_options(self, options):
+ """test helper."""
+ result = argparse.Namespace()
+ parser = Parser(components=[self], namespace=result)
+ parser.parse(options)
+ return result
+
+ def test_option_group(self):
+ """basic option group functionality."""
+ self.options = [OptionGroup(BooleanOption("--foo"),
+ BooleanOption("--bar"),
+ BooleanOption("--baz"),
+ title="group")]
+ result = self._test_options(["--foo", "--bar"])
+ self.assertTrue(result.foo)
+ self.assertTrue(result.bar)
+ self.assertFalse(result.baz)
+
+ def test_exclusive_option_group(self):
+ """parse options from exclusive option group."""
+ self.options = [
+ ExclusiveOptionGroup(BooleanOption("--foo"),
+ BooleanOption("--bar"),
+ BooleanOption("--baz"))]
+ result = self._test_options(["--foo"])
+ self.assertTrue(result.foo)
+ self.assertFalse(result.bar)
+ self.assertFalse(result.baz)
+
+ self.assertRaises(SystemExit,
+ self._test_options, ["--foo", "--bar"])
+
+ def test_required_exclusive_option_group(self):
+ """parse options from required exclusive option group."""
+ self.options = [
+ ExclusiveOptionGroup(BooleanOption("--foo"),
+ BooleanOption("--bar"),
+ BooleanOption("--baz"),
+ required=True)]
+ result = self._test_options(["--foo"])
+ self.assertTrue(result.foo)
+ self.assertFalse(result.bar)
+ self.assertFalse(result.baz)
+
+ self.assertRaises(SystemExit, self._test_options, [])
+
+ def test_option_group(self):
+ """nest option groups."""
+ self.options = [
+ OptionGroup(
+ BooleanOption("--foo"),
+ BooleanOption("--bar"),
+ OptionGroup(
+ BooleanOption("--baz"),
+ BooleanOption("--quux"),
+ ExclusiveOptionGroup(
+ BooleanOption("--test1"),
+ BooleanOption("--test2")),
+ title="inner"),
+ title="outer")]
+ result = self._test_options(["--foo", "--baz", "--test1"])
+ self.assertTrue(result.foo)
+ self.assertFalse(result.bar)
+ self.assertTrue(result.baz)
+ self.assertFalse(result.quux)
+ self.assertTrue(result.test1)
+ self.assertFalse(result.test2)
+
+ self.assertRaises(SystemExit,
+ self._test_options, ["--test1", "--test2"])
+
+
+class TestWildcardSectionGroups(OptionTestCase):
+ config = {
+ "four:one": {
+ "foo": "foo one",
+ "bar": "bar one",
+ "baz": "baz one"
+ },
+ "four:two": {
+ "foo": "foo two",
+ "bar": "bar two"
+ },
+ "five:one": {
+ "foo": "foo one",
+ "bar": "bar one"
+ },
+ "five:two": {
+ "foo": "foo two",
+ "bar": "bar two"
+ },
+ "five:three": {
+ "foo": "foo three",
+ "bar": "bar three"
+ }
+ }
+
+ def setUp(self):
+ self.options = [
+ WildcardSectionGroup(
+ Option(cf=("four:*", "foo")),
+ Option(cf=("four:*", "bar"))),
+ WildcardSectionGroup(
+ Option(cf=("five:*", "foo")),
+ Option(cf=("five:*", "bar")),
+ prefix="",
+ dest="sections")]
+ self.results = argparse.Namespace()
+ new_parser()
+ self.parser = get_parser(components=[self], namespace=self.results)
+
+ @make_config(config)
+ def test_wildcard_section_groups(self, config_file):
+ """parse options from wildcard section groups."""
+ self.parser.parse(["-C", config_file])
+ self.assertEqual(self.results.four_four_one_foo, "foo one")
+ self.assertEqual(self.results.four_four_one_bar, "bar one")
+ self.assertEqual(self.results.four_four_two_foo, "foo two")
+ self.assertEqual(self.results.four_four_two_bar, "bar two")
+ self.assertItemsEqual(self.results.four_sections,
+ ["four:one", "four:two"])
+
+ self.assertEqual(self.results.five_one_foo, "foo one")
+ self.assertEqual(self.results.five_one_bar, "bar one")
+ self.assertEqual(self.results.five_two_foo, "foo two")
+ self.assertEqual(self.results.five_two_bar, "bar two")
+ self.assertEqual(self.results.five_three_foo, "foo three")
+ self.assertEqual(self.results.five_three_bar, "bar three")
+ self.assertItemsEqual(self.results.sections,
+ ["five:one", "five:two", "five:three"])
diff --git a/testsuite/Testsrc/Testlib/TestOptions/TestOptions.py b/testsuite/Testsrc/Testlib/TestOptions/TestOptions.py
new file mode 100644
index 000000000..b76cd6d3a
--- /dev/null
+++ b/testsuite/Testsrc/Testlib/TestOptions/TestOptions.py
@@ -0,0 +1,469 @@
+"""basic option parsing tests."""
+
+import argparse
+import os
+import tempfile
+
+import mock
+
+from Bcfg2.Compat import ConfigParser
+from Bcfg2.Options import Option, PathOption, BooleanOption, Parser, \
+ PositionalArgument, OptionParserException, Common, new_parser, get_parser
+from testsuite.Testsrc.Testlib.TestOptions import OptionTestCase, \
+ make_config, clean_environment
+
+
+class TestBasicOptions(OptionTestCase):
+ """test basic option parsing."""
+ def setUp(self):
+ # parsing options can modify the Option objects themselves.
+ # that's probably bad -- and it's definitely bad if we ever
+ # want to do real on-the-fly config changes -- but it's easier
+ # to leave it as is and set the options on each test.
+ self.options = [
+ BooleanOption("--test-true-boolean", env="TEST_TRUE_BOOLEAN",
+ cf=("test", "true_boolean"), default=True),
+ BooleanOption("--test-false-boolean", env="TEST_FALSE_BOOLEAN",
+ cf=("test", "false_boolean"), default=False),
+ BooleanOption(cf=("test", "true_config_boolean"),
+ default=True),
+ BooleanOption(cf=("test", "false_config_boolean"),
+ default=False),
+ Option("--test-option", env="TEST_OPTION", cf=("test", "option"),
+ default="foo"),
+ PathOption("--test-path-option", env="TEST_PATH_OPTION",
+ cf=("test", "path"), default="/test")]
+
+ @clean_environment
+ def _test_options(self, options=None, env=None, config=None):
+ """helper to test a set of options.
+
+ returns the namespace from parsing the given CLI options with
+ the given config and environment.
+ """
+ if config is not None:
+ config = {"test": config}
+ if options is None:
+ options = []
+
+ @make_config(config)
+ def inner(config_file):
+ """do the actual tests, since py2.4 lacks context managers."""
+ result = argparse.Namespace()
+ parser = Parser(components=[self], namespace=result)
+ parser.parse(argv=["-C", config_file] + options)
+ return result
+
+ if env is not None:
+ for name, value in env.items():
+ os.environ[name] = value
+
+ return inner()
+
+ def test_expand_path(self):
+ """expand ~ in path option."""
+ options = self._test_options(options=["--test-path-option",
+ "~/test"])
+ self.assertEqual(options.test_path_option,
+ os.path.expanduser("~/test"))
+
+ def test_canonicalize_path(self):
+ """get absolute path from path option."""
+ options = self._test_options(options=["--test-path-option",
+ "./test"])
+ self.assertEqual(options.test_path_option,
+ os.path.abspath("./test"))
+
+ def test_default_bool(self):
+ """use the default value of boolean options."""
+ options = self._test_options()
+ self.assertTrue(options.test_true_boolean)
+ self.assertFalse(options.test_false_boolean)
+ self.assertTrue(options.true_config_boolean)
+ self.assertFalse(options.false_config_boolean)
+
+ def test_default(self):
+ """use the default value of an option."""
+ options = self._test_options()
+ self.assertEqual(options.test_option, "foo")
+
+ def test_default_path(self):
+ """use the default value of a path option."""
+ options = self._test_options()
+ self.assertEqual(options.test_path_option, "/test")
+
+ def test_invalid_boolean(self):
+ """set boolean to invalid values."""
+ self.assertRaises(ValueError,
+ self._test_options,
+ config={"true_boolean": "you betcha"})
+ self.assertRaises(ValueError,
+ self._test_options,
+ env={"TEST_TRUE_BOOLEAN": "hell no"})
+
+ def test_set_boolean_in_config(self):
+ """set boolean options in config files."""
+ set_to_defaults = {"true_boolean": "1",
+ "false_boolean": "0",
+ "true_config_boolean": "yes",
+ "false_config_boolean": "no"}
+ options = self._test_options(config=set_to_defaults)
+ self.assertTrue(options.test_true_boolean)
+ self.assertFalse(options.test_false_boolean)
+ self.assertTrue(options.true_config_boolean)
+ self.assertFalse(options.false_config_boolean)
+
+ set_to_other = {"true_boolean": "false",
+ "false_boolean": "true",
+ "true_config_boolean": "off",
+ "false_config_boolean": "on"}
+ options = self._test_options(config=set_to_other)
+ self.assertFalse(options.test_true_boolean)
+ self.assertTrue(options.test_false_boolean)
+ self.assertFalse(options.true_config_boolean)
+ self.assertTrue(options.false_config_boolean)
+
+ def test_set_in_config(self):
+ """set options in config files."""
+ options = self._test_options(config={"option": "foo"})
+ self.assertEqual(options.test_option, "foo")
+
+ options = self._test_options(config={"option": "bar"})
+ self.assertEqual(options.test_option, "bar")
+
+ def test_set_path_in_config(self):
+ """set path options in config files."""
+ options = self._test_options(config={"path": "/test"})
+ self.assertEqual(options.test_path_option, "/test")
+
+ options = self._test_options(config={"path": "/foo"})
+ self.assertEqual(options.test_path_option, "/foo")
+
+ def test_set_boolean_in_env(self):
+ """set boolean options in environment."""
+ set_to_defaults = {"TEST_TRUE_BOOLEAN": "1",
+ "TEST_FALSE_BOOLEAN": "0"}
+ options = self._test_options(env=set_to_defaults)
+ self.assertTrue(options.test_true_boolean)
+ self.assertFalse(options.test_false_boolean)
+
+ set_to_other = {"TEST_TRUE_BOOLEAN": "false",
+ "TEST_FALSE_BOOLEAN": "true"}
+ options = self._test_options(env=set_to_other)
+ self.assertFalse(options.test_true_boolean)
+ self.assertTrue(options.test_false_boolean)
+
+ def test_set_in_env(self):
+ """set options in environment."""
+ options = self._test_options(env={"TEST_OPTION": "foo"})
+ self.assertEqual(options.test_option, "foo")
+
+ options = self._test_options(env={"TEST_OPTION": "bar"})
+ self.assertEqual(options.test_option, "bar")
+
+ def test_set_path_in_env(self):
+ """set path options in environment."""
+ options = self._test_options(env={"TEST_PATH_OPTION": "/test"})
+ self.assertEqual(options.test_path_option, "/test")
+
+ options = self._test_options(env={"TEST_PATH_OPTION": "/foo"})
+ self.assertEqual(options.test_path_option, "/foo")
+
+ def test_set_boolean_in_cli(self):
+ """set boolean options in CLI options."""
+ # passing the option yields the reverse of the default, no
+ # matter the default
+ options = self._test_options(options=["--test-true-boolean",
+ "--test-false-boolean"])
+ self.assertFalse(options.test_true_boolean)
+ self.assertTrue(options.test_false_boolean)
+
+ def test_set_in_cli(self):
+ """set options in CLI options."""
+ options = self._test_options(options=["--test-option", "foo"])
+ self.assertEqual(options.test_option, "foo")
+
+ options = self._test_options(options=["--test-option", "bar"])
+ self.assertEqual(options.test_option, "bar")
+
+ def test_set_path_in_cli(self):
+ """set path options in CLI options."""
+ options = self._test_options(options=["--test-path-option", "/test"])
+ self.assertEqual(options.test_path_option, "/test")
+
+ options = self._test_options(options=["--test-path-option", "/foo"])
+ self.assertEqual(options.test_path_option, "/foo")
+
+ def test_env_overrides_config_bool(self):
+ """setting boolean option in the environment overrides config file."""
+ config = {"true_boolean": "false",
+ "false_boolean": "true"}
+ env = {"TEST_TRUE_BOOLEAN": "yes",
+ "TEST_FALSE_BOOLEAN": "no"}
+ options = self._test_options(config=config, env=env)
+ self.assertTrue(options.test_true_boolean)
+ self.assertFalse(options.test_false_boolean)
+
+ def test_env_overrides_config(self):
+ """setting option in the environment overrides config file."""
+ options = self._test_options(config={"option": "bar"},
+ env={"TEST_OPTION": "baz"})
+ self.assertEqual(options.test_option, "baz")
+
+ def test_env_overrides_config_path(self):
+ """setting path option in the environment overrides config file."""
+ options = self._test_options(config={"path": "/foo"},
+ env={"TEST_PATH_OPTION": "/bar"})
+ self.assertEqual(options.test_path_option, "/bar")
+
+ def test_cli_overrides_config_bool(self):
+ """setting boolean option in the CLI overrides config file."""
+ config = {"true_boolean": "on",
+ "false_boolean": "off"}
+ options = ["--test-true-boolean", "--test-false-boolean"]
+ options = self._test_options(options=options, config=config)
+ self.assertFalse(options.test_true_boolean)
+ self.assertTrue(options.test_false_boolean)
+
+ def test_cli_overrides_config(self):
+ """setting option in the CLI overrides config file."""
+ options = self._test_options(options=["--test-option", "baz"],
+ config={"option": "bar"})
+ self.assertEqual(options.test_option, "baz")
+
+ def test_cli_overrides_config_path(self):
+ """setting path option in the CLI overrides config file."""
+ options = self._test_options(options=["--test-path-option", "/bar"],
+ config={"path": "/foo"})
+ self.assertEqual(options.test_path_option, "/bar")
+
+ def test_cli_overrides_env_bool(self):
+ """setting boolean option in the CLI overrides environment."""
+ env = {"TEST_TRUE_BOOLEAN": "0",
+ "TEST_FALSE_BOOLEAN": "1"}
+ options = ["--test-true-boolean", "--test-false-boolean"]
+ options = self._test_options(options=options, env=env)
+ self.assertFalse(options.test_true_boolean)
+ self.assertTrue(options.test_false_boolean)
+
+ def test_cli_overrides_env(self):
+ """setting option in the CLI overrides environment."""
+ options = self._test_options(options=["--test-option", "baz"],
+ env={"TEST_OPTION": "bar"})
+ self.assertEqual(options.test_option, "baz")
+
+ def test_cli_overrides_env_path(self):
+ """setting path option in the CLI overrides environment."""
+ options = self._test_options(options=["--test-path-option", "/bar"],
+ env={"TEST_PATH_OPTION": "/foo"})
+ self.assertEqual(options.test_path_option, "/bar")
+
+ def test_cli_overrides_all_bool(self):
+ """setting boolean option in the CLI overrides everything else."""
+ config = {"true_boolean": "no",
+ "false_boolean": "yes"}
+ env = {"TEST_TRUE_BOOLEAN": "0",
+ "TEST_FALSE_BOOLEAN": "1"}
+ options = ["--test-true-boolean", "--test-false-boolean"]
+ options = self._test_options(options=options, env=env)
+ self.assertFalse(options.test_true_boolean)
+ self.assertTrue(options.test_false_boolean)
+
+ def test_cli_overrides_all(self):
+ """setting option in the CLI overrides everything else."""
+ options = self._test_options(options=["--test-option", "baz"],
+ env={"TEST_OPTION": "bar"},
+ config={"test": "quux"})
+ self.assertEqual(options.test_option, "baz")
+
+ def test_cli_overrides_all_path(self):
+ """setting path option in the CLI overrides everything else."""
+ options = self._test_options(options=["--test-path-option", "/bar"],
+ env={"TEST_PATH_OPTION": "/foo"},
+ config={"path": "/baz"})
+ self.assertEqual(options.test_path_option, "/bar")
+
+ @make_config()
+ def _test_dest(self, *args, **kwargs):
+ """helper to test that ``dest`` is set properly."""
+ args = list(args)
+ expected = args.pop(0)
+ config_file = args.pop()
+
+ sentinel = object()
+ kwargs["default"] = sentinel
+
+ result = argparse.Namespace()
+ parser = Parser(namespace=result)
+ parser.add_options([Option(*args, **kwargs)])
+ parser.parse(["-C", config_file])
+
+ self.assertTrue(hasattr(result, expected))
+ self.assertEqual(getattr(result, expected), sentinel)
+
+ def test_explicit_dest(self):
+ """set the ``dest`` of an option explicitly."""
+ self._test_dest("bar", dest="bar")
+
+ def test_dest_from_env_var(self):
+ """set the ``dest`` of an option from the env var name."""
+ self._test_dest("foo", env="FOO")
+
+ def test_dest_from_cf(self):
+ """set the ``dest`` of an option from the config option."""
+ self._test_dest("foo_bar", cf=("test", "foo-bar"))
+
+ def test_dest_from_cli(self):
+ """set the ``dest`` of an option from the CLI option."""
+ self._test_dest("test_foo", "--test-foo")
+
+ def test_dest_from_all(self):
+ """set the ``dest`` of an option from the best of multiple sources."""
+ self._test_dest("foo_baz", cf=("test", "foo-bar"), env="FOO_BAZ")
+ self._test_dest("xyzzy",
+ "--xyzzy", cf=("test", "foo-bar"), env="FOO_BAZ")
+ self._test_dest("quux",
+ "--xyzzy", cf=("test", "foo-bar"), env="FOO_BAZ",
+ dest="quux")
+
+ @make_config()
+ def test_positional_args(self, config_file):
+ """get values from positional arguments."""
+ result = argparse.Namespace()
+ parser = Parser(namespace=result)
+ parser.add_options([PositionalArgument("single")])
+ parser.parse(["-C", config_file, "single"])
+ self.assertEqual(result.single, "single")
+
+ result = argparse.Namespace()
+ parser = Parser(namespace=result)
+ parser.add_options([PositionalArgument("one"),
+ PositionalArgument("two")])
+ parser.parse(["-C", config_file, "one", "two"])
+ self.assertEqual(result.one, "one")
+ self.assertEqual(result.two, "two")
+
+ def test_duplicate_cli_option(self):
+ """add duplicate CLI option."""
+ parser = Parser(components=[self])
+ self.assertRaises(
+ argparse.ArgumentError,
+ parser.add_options,
+ [Option("--test-option")])
+
+ def test_duplicate_env_option(self):
+ """add duplicate environment option."""
+ parser = Parser(components=[self])
+ self.assertRaises(
+ OptionParserException,
+ parser.add_options,
+ [Option(env="TEST_OPTION")])
+
+ def test_duplicate_cf_option(self):
+ """add duplicate config file option."""
+ parser = Parser(components=[self])
+ self.assertRaises(
+ OptionParserException,
+ parser.add_options,
+ [Option(cf=("test", "option"))])
+
+ @make_config()
+ def test_repository_macro(self, config_file):
+ """fix up <repository> macros."""
+ result = argparse.Namespace()
+ parser = Parser(namespace=result)
+ parser.add_options([PathOption("--test1"),
+ PathOption("--test2"),
+ Common.repository])
+ parser.parse(["-C", config_file, "-Q", "/foo/bar",
+ "--test1", "<repository>/test1",
+ "--test2", "<repository><repository>"])
+ self.assertEqual(result.repository, "/foo/bar")
+ self.assertEqual(result.test1, "/foo/bar/test1")
+ self.assertEqual(result.test2, "/foo/bar/foo/bar")
+
+ @make_config()
+ def test_file_like_path_option(self, config_file):
+ """get file-like object from PathOption."""
+ result = argparse.Namespace()
+ parser = Parser(namespace=result)
+ parser.add_options([PathOption("--test", type=argparse.FileType('r'))])
+
+ fd, name = tempfile.mkstemp()
+ fh = os.fdopen(fd, "w")
+ fh.write("test")
+ fh.close()
+
+ parser.parse(["-C", config_file, "--test", name])
+ self.assertEqual(result.test.name, name)
+ self.assertEqual(result.test.read(), "test")
+
+ @clean_environment
+ @make_config()
+ def test_unknown_options(self, config_file):
+ """error on unknown options."""
+ parser = Parser(components=[self])
+ self.assertRaises(SystemExit,
+ parser.parse,
+ ["-C", config_file, "--not-a-real-option"])
+
+ @clean_environment
+ @make_config()
+ def test_reparse(self, config_file):
+ """reparse options."""
+ result = argparse.Namespace()
+ parser = Parser(components=[self], namespace=result)
+ parser.parse(["-C", config_file])
+ self.assertFalse(result.test_false_boolean)
+
+ parser.parse(["-C", config_file])
+ self.assertFalse(result.test_false_boolean)
+
+ parser.reparse()
+ self.assertFalse(result.test_false_boolean)
+
+ parser.reparse(["-C", config_file, "--test-false-boolean"])
+ self.assertTrue(result.test_false_boolean)
+
+ cfp = ConfigParser.ConfigParser()
+ cfp.add_section("test")
+ cfp.set("test", "false_boolean", "on")
+ parser.parse(["-C", config_file])
+ cfp.write(open(config_file, "w"))
+ self.assertTrue(result.test_false_boolean)
+
+
+class TestParsingHooks(OptionTestCase):
+ """test option parsing hooks."""
+ def setUp(self):
+ self.options_parsed_hook = mock.Mock()
+ self.options = [BooleanOption("--test", default=False)]
+ self.results = argparse.Namespace()
+ new_parser()
+ self.parser = get_parser(components=[self], namespace=self.results)
+
+ @make_config()
+ def test_parsing_hooks(self, config_file):
+ """option parsing hooks are called."""
+ self.parser.parse(["-C", config_file])
+ self.options_parsed_hook.assert_called_with()
+
+
+class TestEarlyParsingHooks(OptionTestCase):
+ """test early option parsing hooks."""
+ parse_first = True
+
+ def setUp(self):
+ self.component_parsed_hook = mock.Mock()
+ self.options = [BooleanOption("--early-test", default=False)]
+ self.results = argparse.Namespace()
+ new_parser()
+ self.parser = get_parser(components=[self], namespace=self.results)
+
+ @make_config()
+ def test_parsing_hooks(self, config_file):
+ """early option parsing hooks are called."""
+ self.parser.parse(["-C", config_file, "--early-test"])
+ self.assertEqual(self.component_parsed_hook.call_count, 1)
+ early_opts = self.component_parsed_hook.call_args[0][0]
+ self.assertTrue(early_opts.early_test)
diff --git a/testsuite/Testsrc/Testlib/TestOptions/TestSubcommands.py b/testsuite/Testsrc/Testlib/TestOptions/TestSubcommands.py
new file mode 100644
index 000000000..35da909cb
--- /dev/null
+++ b/testsuite/Testsrc/Testlib/TestOptions/TestSubcommands.py
@@ -0,0 +1,141 @@
+"""test subcommand option parsing."""
+
+import argparse
+import sys
+
+from Bcfg2.Compat import StringIO
+from Bcfg2.Options import Option, get_parser, new_parser, Subcommand, \
+ Subparser, CommandRegistry
+import Bcfg2.Options.Subcommands
+
+from testsuite.Testsrc.Testlib.TestOptions import make_config, OptionTestCase
+
+
+class MockSubcommand(Subcommand):
+ """fake subcommand that just records the options it was called with."""
+ run_options = None
+
+ def run(self, setup):
+ self.__class__.run_options = setup
+
+
+class One(MockSubcommand):
+ """fake subcommand for testing."""
+ options = [Option("--test-one")]
+
+
+class Two(MockSubcommand):
+ """fake subcommand for testing."""
+ options = [Option("--test-two")]
+
+
+def local_subclass(cls):
+ """get a subclass of ``cls`` that adds no functionality.
+
+ This can be used to subclass the various test classes above so
+ that their options don't get modified by option parsing.
+ """
+ return type("Local%s" % cls.__name__, (cls,), {})
+
+
+class TestSubcommands(OptionTestCase):
+ """tests for subcommands and subparsers."""
+
+ def setUp(self):
+ self.registry = CommandRegistry()
+
+ self.one = local_subclass(One)
+ self.two = local_subclass(Two)
+
+ self.registry.register_command(self.one)
+ self.registry.register_command(self.two)
+
+ self.result = argparse.Namespace()
+ Bcfg2.Options.Subcommands.master_setup = self.result
+
+ new_parser()
+ self.parser = get_parser(namespace=self.result,
+ components=[self])
+ self.parser.add_options(self.registry.subcommand_options)
+
+ def test_register_commands(self):
+ """register subcommands."""
+ registry = CommandRegistry()
+ registry.register_commands(globals().values(),
+ parent=MockSubcommand)
+ self.assertItemsEqual(registry.commands.keys(),
+ ["one", "two", "help"])
+ self.assertIsInstance(registry.commands['one'], One)
+ self.assertIsInstance(registry.commands['two'], Two)
+
+ @make_config()
+ def test_get_subcommand(self, config_file):
+ """parse simple subcommands."""
+ self.parser.parse(["-C", config_file, "localone"])
+ self.assertEqual(self.result.subcommand, "localone")
+
+ def test_subcommand_usage(self):
+ """sane usage message from subcommands."""
+ self.assertEqual(
+ One().usage(),
+ "one [--test-one TEST_ONE] - fake subcommand for testing.")
+
+ # subclasses do not inherit the docstring from the parent, so
+ # this tests a command subclass without a docstring, even
+ # though that should never happen due to the pylint tests.
+ self.assertEqual(self.one().usage().strip(),
+ "localone [--test-one TEST_ONE]")
+
+ @make_config()
+ def test_help(self, config_file):
+ """sane help message from subcommand registry."""
+ self.parser.parse(["-C", config_file, "help"])
+ old_stdout = sys.stdout
+ sys.stdout = StringIO()
+ self.assertIn(self.registry.runcommand(), [0, None])
+ help_message = sys.stdout.getvalue().splitlines()
+ sys.stdout = old_stdout
+
+ # the help message will look like:
+ #
+ # localhelp [<command>]
+ # localone [--test-one TEST_ONE]
+ # localtwo [--test-two TEST_TWO]
+ commands = []
+ command_help = {
+ "help": self.registry.help.usage(),
+ "localone": self.one().usage(),
+ "localtwo": self.two().usage()}
+ for line in help_message:
+ command = line.split()[0]
+ commands.append(command)
+ if command not in command_help:
+ self.fail("Got help for unknown command %s: %s" %
+ (command, line))
+ self.assertEqual(line, command_help[command])
+ self.assertItemsEqual(commands, command_help.keys())
+
+ @make_config()
+ def test_subcommand_help(self, config_file):
+ """get help message on a single command."""
+ self.parser.parse(["-C", config_file, "help", "localone"])
+ old_stdout = sys.stdout
+ sys.stdout = StringIO()
+ self.assertIn(self.registry.runcommand(), [0, None])
+ help_message = sys.stdout.getvalue().splitlines()
+ sys.stdout = old_stdout
+
+ self.assertEqual(help_message[0].strip(),
+ "usage: %s" % self.one().usage().strip())
+
+ @make_config()
+ def test_nonexistent_subcommand_help(self, config_file):
+ """get help message on a nonexistent command."""
+ self.parser.parse(["-C", config_file, "help", "blargle"])
+ old_stdout = sys.stdout
+ sys.stdout = StringIO()
+ self.assertNotEqual(self.registry.runcommand(), 0)
+ help_message = sys.stdout.getvalue().splitlines()
+ sys.stdout = old_stdout
+
+ self.assertIn("No such command", help_message[0])
diff --git a/testsuite/Testsrc/Testlib/TestOptions/TestTypes.py b/testsuite/Testsrc/Testlib/TestOptions/TestTypes.py
new file mode 100644
index 000000000..404d67fdc
--- /dev/null
+++ b/testsuite/Testsrc/Testlib/TestOptions/TestTypes.py
@@ -0,0 +1,111 @@
+"""test builtin option types."""
+
+import argparse
+
+from mock import patch
+
+from Bcfg2.Options import Option, Types, Parser
+from testsuite.common import Bcfg2TestCase
+
+
+class TestOptionTypes(Bcfg2TestCase):
+ """test builtin option types."""
+ def setUp(self):
+ self.options = None
+
+ def _test_options(self, options):
+ """helper to test option types.
+
+ this expects that self.options is set to a single option named
+ test. The value of that option is returned.
+ """
+ result = argparse.Namespace()
+ parser = Parser(components=[self], namespace=result)
+ parser.parse(options)
+ return result.test
+
+ def test_comma_list(self):
+ """parse comma-list values."""
+ self.options = [Option("--test", type=Types.comma_list)]
+
+ expected = ["one", "two", "three"]
+ self.assertItemsEqual(self._test_options(["--test", "one,two,three"]),
+ expected)
+ self.assertItemsEqual(self._test_options(["--test",
+ "one, two, three"]),
+ expected)
+ self.assertItemsEqual(self._test_options(["--test",
+ "one , two ,three"]),
+ expected)
+ self.assertItemsEqual(self._test_options(["--test", "one two, three"]),
+ ["one two", "three"])
+
+ def test_colon_list(self):
+ """parse colon-list values."""
+ self.options = [Option("--test", type=Types.colon_list)]
+ self.assertItemsEqual(self._test_options(["--test", "one:two three"]),
+ ["one", "two three"])
+
+ def test_comma_dict(self):
+ """parse comma-dict values."""
+ self.options = [Option("--test", type=Types.comma_dict)]
+ expected = {
+ "one": True,
+ "two": 2,
+ "three": "three",
+ "four": False}
+ self.assertDictEqual(
+ self._test_options(["--test",
+ "one=yes, two=2 , three=three,four=no"]),
+ expected)
+
+ self.assertDictEqual(
+ self._test_options(["--test", "one,two=2,three=three,four=off"]),
+ expected)
+
+ def test_anchored_regex_list(self):
+ """parse regex lists."""
+ self.options = [Option("--test", type=Types.anchored_regex_list)]
+ self.assertItemsEqual(
+ [r.pattern for r in self._test_options(["--test", r'\d+ \s*'])],
+ [r'^\d+$', r'^\s*$'])
+ self.assertRaises(SystemExit,
+ self._test_options, ["--test", '(]'])
+
+ def test_octal(self):
+ """parse octal options."""
+ self.options = [Option("--test", type=Types.octal)]
+ self.assertEqual(self._test_options(["--test", "0777"]), 511)
+ self.assertEqual(self._test_options(["--test", "133114255"]), 23894189)
+
+ @patch("pwd.getpwnam")
+ def test_username(self, mock_getpwnam):
+ """parse username options."""
+ self.options = [Option("--test", type=Types.username)]
+ mock_getpwnam.return_value = ("test", '********', 1001, 1001,
+ "Test user", "/home/test", "/bin/bash")
+ self.assertEqual(self._test_options(["--test", "1001"]), 1001)
+ self.assertEqual(self._test_options(["--test", "test"]), 1001)
+
+ @patch("grp.getgrnam")
+ def test_groupname(self, mock_getpwnam):
+ """parse group name options."""
+ self.options = [Option("--test", type=Types.groupname)]
+ mock_getpwnam.return_value = ("test", '*', 1001, ["test"])
+ self.assertEqual(self._test_options(["--test", "1001"]), 1001)
+ self.assertEqual(self._test_options(["--test", "test"]), 1001)
+
+ def test_timeout(self):
+ """parse timeout options."""
+ self.options = [Option("--test", type=Types.timeout)]
+ self.assertEqual(self._test_options(["--test", "1.0"]), 1.0)
+ self.assertEqual(self._test_options(["--test", "1"]), 1.0)
+ self.assertEqual(self._test_options(["--test", "0"]), None)
+
+ def test_size(self):
+ """parse human-readable size options."""
+ self.options = [Option("--test", type=Types.size)]
+ self.assertEqual(self._test_options(["--test", "5k"]), 5120)
+ self.assertEqual(self._test_options(["--test", "5"]), 5)
+ self.assertRaises(SystemExit,
+ self._test_options, ["--test", "g5m"])
diff --git a/testsuite/Testsrc/Testlib/TestOptions/TestWildcards.py b/testsuite/Testsrc/Testlib/TestOptions/TestWildcards.py
new file mode 100644
index 000000000..da196a912
--- /dev/null
+++ b/testsuite/Testsrc/Testlib/TestOptions/TestWildcards.py
@@ -0,0 +1,47 @@
+"""test wildcard options."""
+
+import argparse
+
+from Bcfg2.Options import Option, Parser
+from testsuite.Testsrc.Testlib.TestOptions import OptionTestCase, make_config
+
+
+class TestWildcardOptions(OptionTestCase):
+ """test parsing wildcard options."""
+ config = {
+ "foo": {
+ "test1": "test1",
+ "test2": "test2",
+ "thing1": "thing1",
+ "thing2": "thing2",
+ "foo": "foo"
+ }
+ }
+
+ def setUp(self):
+ # parsing options can modify the Option objects themselves.
+ # that's probably bad -- and it's definitely bad if we ever
+ # want to do real on-the-fly config changes -- but it's easier
+ # to leave it as is and set the options on each test.
+ self.options = [
+ Option(cf=("foo", "*"), dest="all"),
+ Option(cf=("foo", "test*"), dest="test"),
+ Option(cf=("foo", "bogus*"), dest="unmatched"),
+ Option(cf=("bar", "*"), dest="no_section"),
+ Option(cf=("foo", "foo"))]
+
+ @make_config(config)
+ def test_wildcard_options(self, config_file):
+ """parse wildcard options."""
+ result = argparse.Namespace()
+ parser = Parser(components=[self], namespace=result)
+ parser.parse(argv=["-C", config_file])
+
+ self.assertDictEqual(result.all, {"test1": "test1",
+ "test2": "test2",
+ "thing1": "thing1",
+ "thing2": "thing2"})
+ self.assertDictEqual(result.test, {"test1": "test1",
+ "test2": "test2"})
+ self.assertDictEqual(result.unmatched, {})
+ self.assertDictEqual(result.no_section, {})
diff --git a/testsuite/Testsrc/Testlib/TestOptions/Two.py b/testsuite/Testsrc/Testlib/TestOptions/Two.py
new file mode 100644
index 000000000..189e0817f
--- /dev/null
+++ b/testsuite/Testsrc/Testlib/TestOptions/Two.py
@@ -0,0 +1,6 @@
+"""Test module for component loading."""
+
+
+class Two(object):
+ """Test class for component loading."""
+ pass
diff --git a/testsuite/Testsrc/Testlib/TestOptions/__init__.py b/testsuite/Testsrc/Testlib/TestOptions/__init__.py
new file mode 100644
index 000000000..b051f65e5
--- /dev/null
+++ b/testsuite/Testsrc/Testlib/TestOptions/__init__.py
@@ -0,0 +1,85 @@
+"""helper functions for option testing."""
+
+import os
+import tempfile
+
+from Bcfg2.Compat import wraps, ConfigParser
+from Bcfg2.Options import Parser
+from testsuite.common import Bcfg2TestCase
+
+
+class make_config(object): # pylint: disable=invalid-name
+ """decorator to create a temporary config file from a dict.
+
+ The filename of the temporary config file is added as the last
+ positional argument to the function call.
+ """
+ def __init__(self, config_data=None):
+ self.config_data = config_data or {}
+
+ def __call__(self, func):
+ @wraps(func)
+ def inner(*args, **kwargs):
+ """decorated function."""
+ cfp = ConfigParser.ConfigParser()
+ for section, options in self.config_data.items():
+ cfp.add_section(section)
+ for key, val in options.items():
+ cfp.set(section, key, val)
+ fd, name = tempfile.mkstemp()
+ config_file = os.fdopen(fd, 'w')
+ cfp.write(config_file)
+ config_file.close()
+
+ args = list(args) + [name]
+ rv = func(*args, **kwargs)
+ os.unlink(name)
+ return rv
+
+ return inner
+
+
+def clean_environment(func):
+ """decorator that unsets any environment variables used by options.
+
+ The list of options is taken from the first argument, which is
+ presumed to be ``self``. The variables are restored at the end of
+ the function.
+ """
+ @wraps(func)
+ def inner(self, *args, **kwargs):
+ """decorated function."""
+ envvars = {}
+ for opt in self.options:
+ if opt.env is not None:
+ envvars[opt.env] = os.environ.get(opt.env)
+ if opt.env in os.environ:
+ del os.environ[opt.env]
+ rv = func(self, *args, **kwargs)
+ for name, val in envvars.items():
+ if val is None and name in os.environ:
+ del os.environ[name]
+ elif val is not None:
+ os.environ[name] = val
+ return rv
+
+ return inner
+
+
+class OptionTestCase(Bcfg2TestCase):
+ """test case that doesn't mock out config file reading."""
+
+ @classmethod
+ def setUpClass(cls):
+ # ensure that the option parser actually reads config files
+ Parser.unit_test = False
+
+ @classmethod
+ def tearDownClass(cls):
+ Parser.unit_test = True
+
+
+
+# TODO:
+# * subcommands
+# * common options
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testbase.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testbase.py
index f135a0197..290a7c092 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testbase.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testbase.py
@@ -22,6 +22,7 @@ class TestPlugin(TestDebuggable):
def setUp(self):
TestDebuggable.setUp(self)
set_setup_default("filemonitor", MagicMock())
+ set_setup_default("repository", datastore)
def get_obj(self, core=None):
if core is None:
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testhelpers.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testhelpers.py
index 37beaa26c..5a82100d0 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testhelpers.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testhelpers.py
@@ -55,6 +55,7 @@ class TestDatabaseBacked(TestPlugin):
def setUp(self):
TestPlugin.setUp(self)
set_setup_default("%s_db" % self.test_obj.__name__.lower(), False)
+ set_setup_default("db_engine", None)
@skipUnless(HAS_DJANGO, "Django not found")
def test__use_db(self):
diff --git a/testsuite/Testsrc/Testlib/TestUtils.py b/testsuite/Testsrc/Testlib/TestUtils.py
index 349d6cd40..4bed67248 100644
--- a/testsuite/Testsrc/Testlib/TestUtils.py
+++ b/testsuite/Testsrc/Testlib/TestUtils.py
@@ -1,9 +1,5 @@
import os
import sys
-import copy
-import lxml.etree
-import subprocess
-from mock import Mock, MagicMock, patch
from Bcfg2.Utils import *
# add all parent testsuite directories to sys.path to allow (most)
diff --git a/testsuite/Testsrc/__init__.py b/testsuite/Testsrc/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/testsuite/Testsrc/__init__.py