From 05dace442d2d6123703910ea80aec6e989c65009 Mon Sep 17 00:00:00 2001 From: Alfredo Deza Date: Thu, 27 Oct 2016 15:39:12 -0400 Subject: [PATCH] tests: create the conftest file that does all the magic Signed-off-by: Alfredo Deza Resolves: pytest#harness --- tests/conftest.py | 101 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 tests/conftest.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..0c054050f --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,101 @@ +import os + +import pytest +import imp + + +def pytest_addoption(parser): + default = 'scenario.py' + parser.addoption( + "--scenario", + action="store", + default=default, + help="YAML file defining scenarios to test. Currently defaults to: %s" % default + ) + + +def load_scenario_config(filepath, **kw): + ''' + Creates a configuration dictionary from a file. + + :param filepath: The path to the file. + ''' + + abspath = os.path.abspath(os.path.expanduser(filepath)) + conf_dict = {} + if not os.path.isfile(abspath): + raise RuntimeError('`%s` is not a file.' % abspath) + + # First, make sure the code will actually compile (and has no SyntaxErrors) + with open(abspath, 'rb') as f: + compiled = compile(f.read(), abspath, 'exec') + + # Next, attempt to actually import the file as a module. + # This provides more verbose import-related error reporting than exec() + absname, _ = os.path.splitext(abspath) + basepath, module_name = absname.rsplit(os.sep, 1) + imp.load_module( + module_name, + *imp.find_module(module_name, [basepath]) + ) + + # If we were able to import as a module, actually exec the compiled code + exec(compiled, globals(), conf_dict) + conf_dict['__file__'] = abspath + return conf_dict + + +def pytest_configure_node(node): + node_id = node.slaveinput['slaveid'] + scenario_path = os.path.abspath(node.config.getoption('--scenario')) + scenario = load_scenario_config(scenario_path) + node.slaveinput['node_config'] = scenario['nodes'][node_id] + node.slaveinput['scenario_config'] = scenario + + +@pytest.fixture(scope='session') +def node_config(request): + return request.config.slaveinput['node_config'] + + +@pytest.fixture(scope="session") +def scenario_config(request): + return request.config.slaveinput['scenario_config'] + + +def pytest_report_header(config): + """ + Hook to add extra information about the execution environment and to be + able to debug what did the magical args got expanded to + """ + lines = [] + scenario_path = str(config.rootdir.join(config.getoption('--scenario'))) + if not config.remote_execution: + lines.append('execution environment: local') + else: + lines.append('execution environment: remote') + lines.append('loaded scenario: %s' % scenario_path) + lines.append('expanded args: %s' % config.extended_args) + return lines + + +def pytest_cmdline_preparse(args, config): + # Note: we can only do our magical args expansion if we aren't already in + # a remote node via xdist/execnet so return quickly if we can't do magic. + # TODO: allow setting an environment variable that helps to skip this kind + # of magical argument expansion + if os.getcwd().endswith('pyexecnetcache'): + return + + scenario_path = os.path.abspath(config.getoption('--scenario')) + + scenarios = load_scenario_config(scenario_path, args=args) + rsync_dir = os.path.dirname(str(config.rootdir.join('functional'))) + test_path = str(config.rootdir.join('functional/tests')) + nodes = [] + config.remote_execution = True + for node in scenarios.get('nodes', []): + nodes.append('--tx') + nodes.append('vagrant_ssh={node_name}//id={node_name}'.format(node_name=node)) + args[:] = args + ['--max-slave-restart', '0', '--dist=each'] + nodes + ['--rsyncdir', rsync_dir, test_path] + config.extended_args = ' '.join(args)