diff --git a/test/runner/injector/python.py b/test/runner/injector/python.py index 1822697ff0e..d74b955f4aa 100755 --- a/test/runner/injector/python.py +++ b/test/runner/injector/python.py @@ -10,8 +10,8 @@ def main(): name = os.path.basename(__file__) args = [sys.executable] - coverage_config = os.environ.get('_ANSIBLE_COVERAGE_CONFIG') - coverage_output = os.environ.get('_ANSIBLE_COVERAGE_OUTPUT') + coverage_config = os.environ.get('COVERAGE_CONF') + coverage_output = os.environ.get('COVERAGE_FILE') if coverage_config: if coverage_output: diff --git a/test/runner/lib/executor.py b/test/runner/lib/executor.py index 8c4e083f2ef..a272e67bb30 100644 --- a/test/runner/lib/executor.py +++ b/test/runner/lib/executor.py @@ -1168,7 +1168,7 @@ def integration_environment(args, target, test_dir, inventory_path, ansible_conf if 'non_local/' in target.aliases: if args.coverage: - display.warning('Skipping coverage reporting for non-local test: %s' % target.name) + display.warning('Skipping coverage reporting on Ansible modules for non-local test: %s' % target.name) env.update(dict(ANSIBLE_TEST_REMOTE_INTERPRETER='')) @@ -1211,8 +1211,8 @@ def command_integration_script(args, target, test_dir, inventory_path, temp_path if config_path: cmd += ['-e', '@%s' % config_path] - coverage = args.coverage and 'non_local/' not in target.aliases - intercept_command(args, cmd, target_name=target.name, env=env, cwd=cwd, temp_path=temp_path, coverage=coverage) + module_coverage = 'non_local/' not in target.aliases + intercept_command(args, cmd, target_name=target.name, env=env, cwd=cwd, temp_path=temp_path, module_coverage=module_coverage) def command_integration_role(args, target, start_at_task, test_dir, inventory_path, temp_path): @@ -1295,8 +1295,8 @@ def command_integration_role(args, target, start_at_task, test_dir, inventory_pa env['ANSIBLE_ROLES_PATH'] = os.path.abspath(os.path.join(test_env.integration_dir, 'targets')) - coverage = args.coverage and 'non_local/' not in target.aliases - intercept_command(args, cmd, target_name=target.name, env=env, cwd=cwd, temp_path=temp_path, coverage=coverage) + module_coverage = 'non_local/' not in target.aliases + intercept_command(args, cmd, target_name=target.name, env=env, cwd=cwd, temp_path=temp_path, module_coverage=module_coverage) def command_units(args): diff --git a/test/runner/lib/util.py b/test/runner/lib/util.py index 25b16058331..6d2308772f1 100644 --- a/test/runner/lib/util.py +++ b/test/runner/lib/util.py @@ -163,12 +163,13 @@ def cleanup_python_paths(): shutil.rmtree(path) -def get_coverage_environment(args, target_name, version, temp_path): +def get_coverage_environment(args, target_name, version, temp_path, module_coverage): """ :type args: TestConfig :type target_name: str :type version: str :type temp_path: str + :type module_coverage: bool :rtype: dict[str, str] """ if temp_path: @@ -187,17 +188,26 @@ def get_coverage_environment(args, target_name, version, temp_path): args.command, target_name, args.coverage_label or 'local-%s' % version, 'python-%s' % version)) if args.coverage_check: + # cause the 'coverage' module to be found, but not imported or enabled coverage_file = '' + # Enable code coverage collection on local Python programs (this does not include Ansible modules). + # Used by the injectors in test/runner/injector/ to support code coverage. + # Used by unit tests in test/units/conftest.py to support code coverage. + # The COVERAGE_FILE variable is also used directly by the 'coverage' module. env = dict( - # both AnsiballZ and the ansible-test coverage injector rely on this - _ANSIBLE_COVERAGE_CONFIG=config_file, - # used during AnsiballZ wrapper creation to set COVERAGE_FILE for the module - _ANSIBLE_COVERAGE_OUTPUT=coverage_file, - # handle cases not covered by the AnsiballZ wrapper creation above + COVERAGE_CONF=config_file, COVERAGE_FILE=coverage_file, ) + if module_coverage: + # Enable code coverage collection on Ansible modules (both local and remote). + # Used by the AnsiballZ wrapper generator in lib/ansible/executor/module_common.py to support code coverage. + env.update(dict( + _ANSIBLE_COVERAGE_CONFIG=config_file, + _ANSIBLE_COVERAGE_OUTPUT=coverage_file, + )) + return env @@ -277,7 +287,8 @@ def generate_pip_command(python): return [python, '-m', 'pip.__main__'] -def intercept_command(args, cmd, target_name, env, capture=False, data=None, cwd=None, python_version=None, temp_path=None, coverage=None, virtualenv=None): +def intercept_command(args, cmd, target_name, env, capture=False, data=None, cwd=None, python_version=None, temp_path=None, module_coverage=True, + virtualenv=None): """ :type args: TestConfig :type cmd: collections.Iterable[str] @@ -288,16 +299,13 @@ def intercept_command(args, cmd, target_name, env, capture=False, data=None, cwd :type cwd: str | None :type python_version: str | None :type temp_path: str | None - :type coverage: bool | None + :type module_coverage: bool :type virtualenv: str | None :rtype: str | None, str | None """ if not env: env = common_environment() - if coverage is None: - coverage = args.coverage - cmd = list(cmd) version = python_version or args.python_version interpreter = virtualenv or find_python(version) @@ -313,9 +321,9 @@ def intercept_command(args, cmd, target_name, env, capture=False, data=None, cwd env['ANSIBLE_TEST_PYTHON_VERSION'] = version env['ANSIBLE_TEST_PYTHON_INTERPRETER'] = interpreter - if coverage: + if args.coverage: # add the necessary environment variables to enable code coverage collection - env.update(get_coverage_environment(args, target_name, version, temp_path)) + env.update(get_coverage_environment(args, target_name, version, temp_path, module_coverage)) return run_command(args, cmd, capture=capture, env=env, data=data, cwd=cwd) diff --git a/test/units/conftest.py b/test/units/conftest.py index 0fb7955081f..bc2bd646ab2 100644 --- a/test/units/conftest.py +++ b/test/units/conftest.py @@ -24,12 +24,12 @@ def pytest_configure(): coverage_instances.append(obj) if not coverage_instances: - coverage_config = os.environ.get('_ANSIBLE_COVERAGE_CONFIG') + coverage_config = os.environ.get('COVERAGE_CONF') if not coverage_config: return - coverage_output = os.environ.get('_ANSIBLE_COVERAGE_OUTPUT') + coverage_output = os.environ.get('COVERAGE_FILE') if not coverage_output: return