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)