diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index eb690592ac6..00000000000 --- a/.coveragerc +++ /dev/null @@ -1,26 +0,0 @@ -# This configuration file is used for manual execution of coverage -# as well as for tests run through ansible-test. - -[run] -branch = True - -# Enable concurrency. This also enables parallel mode, which results in -# multiple coverage files being created. Concurrency allows us to collect -# results from multiple tests simultaneously, as well as supporting multiple -# test runs, such as from integration tests. -concurrency = multiprocessing -parallel = True - -# When running tests through ansible-test, this option is overridden by -# the COVERAGE_FILE environment variable. This option is present for -# convenience when running coverage manually from this directory. -data_file = test/results/coverage/coverage - -# Don't collect or report code coverage from files matching these patterns. -omit = - */python*/dist-packages/* - */python*/site-packages/* - */python*/distutils/* - */pyshared/* - */pytest - */AnsiballZ_*.py diff --git a/MANIFEST.in b/MANIFEST.in index 137483fa0e1..863b286037c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,6 @@ include README.rst include COPYING include SYMLINK_CACHE.json include requirements.txt -include .coveragerc include shippable.yml include bin/ansible-test include examples/hosts diff --git a/test/lib/ansible_test/_data/coveragerc b/test/lib/ansible_test/_data/coveragerc new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/lib/ansible_test/_internal/classification.py b/test/lib/ansible_test/_internal/classification.py index 1ae4f72ed2b..b2beee5096e 100644 --- a/test/lib/ansible_test/_internal/classification.py +++ b/test/lib/ansible_test/_internal/classification.py @@ -836,7 +836,6 @@ class PathMapper: if path in ( 'shippable.yml', - '.coveragerc', ): return all_tests(self.args) # test infrastructure, run all tests diff --git a/test/lib/ansible_test/_internal/cover.py b/test/lib/ansible_test/_internal/cover.py index a768a9d93b2..968f8ebf8aa 100644 --- a/test/lib/ansible_test/_internal/cover.py +++ b/test/lib/ansible_test/_internal/cover.py @@ -14,6 +14,7 @@ from .util import ( display, ApplicationError, common_environment, + ANSIBLE_TEST_DATA_ROOT, ) from .util_common import ( @@ -37,6 +38,7 @@ from .data import ( COVERAGE_DIR = 'test/results/coverage' COVERAGE_FILE = os.path.join(COVERAGE_DIR, 'coverage') COVERAGE_GROUPS = ('command', 'target', 'environment', 'version') +COVERAGE_CONFIG_PATH = os.path.join(ANSIBLE_TEST_DATA_ROOT, 'coveragerc') def command_coverage_combine(args): @@ -239,7 +241,7 @@ def command_coverage_report(args): env = common_environment() env.update(dict(COVERAGE_FILE=output_file)) - run_command(args, env=env, cmd=['coverage', 'report'] + options) + run_command(args, env=env, cmd=['coverage', 'report', '--rcfile', COVERAGE_CONFIG_PATH] + options) def command_coverage_html(args): @@ -252,7 +254,7 @@ def command_coverage_html(args): dir_name = 'test/results/reports/%s' % os.path.basename(output_file) env = common_environment() env.update(dict(COVERAGE_FILE=output_file)) - run_command(args, env=env, cmd=['coverage', 'html', '-i', '-d', dir_name]) + run_command(args, env=env, cmd=['coverage', 'html', '--rcfile', COVERAGE_CONFIG_PATH, '-i', '-d', dir_name]) def command_coverage_xml(args): @@ -265,7 +267,7 @@ def command_coverage_xml(args): xml_name = 'test/results/reports/%s.xml' % os.path.basename(output_file) env = common_environment() env.update(dict(COVERAGE_FILE=output_file)) - run_command(args, env=env, cmd=['coverage', 'xml', '-i', '-o', xml_name]) + run_command(args, env=env, cmd=['coverage', 'xml', '--rcfile', COVERAGE_CONFIG_PATH, '-i', '-o', xml_name]) def command_coverage_erase(args): diff --git a/test/lib/ansible_test/_internal/coverage_util.py b/test/lib/ansible_test/_internal/coverage_util.py index d44bf5bafb8..355990167d1 100644 --- a/test/lib/ansible_test/_internal/coverage_util.py +++ b/test/lib/ansible_test/_internal/coverage_util.py @@ -13,7 +13,7 @@ from .config import ( ) from .util import ( - COVERAGE_CONFIG_PATH, + COVERAGE_CONFIG_NAME, remove_tree, ) @@ -35,16 +35,18 @@ def coverage_context(args): # type: (TestConfig) -> None def coverage_setup(args): # type: (TestConfig) -> None """Set up code coverage configuration before running tests.""" - if args.coverage and data_context().content.collection: - coverage_config = generate_collection_coverage_config(args) + if not args.coverage: + return + + coverage_config = generate_coverage_config(args) - if args.explain: - args.coverage_config_base_path = '/tmp/coverage-temp-dir' - else: - args.coverage_config_base_path = tempfile.mkdtemp() + if args.explain: + args.coverage_config_base_path = '/tmp/coverage-temp-dir' + else: + args.coverage_config_base_path = tempfile.mkdtemp() - with open(os.path.join(args.coverage_config_base_path, COVERAGE_CONFIG_PATH), 'w') as coverage_config_path_fd: - coverage_config_path_fd.write(coverage_config) + with open(os.path.join(args.coverage_config_base_path, COVERAGE_CONFIG_NAME), 'w') as coverage_config_path_fd: + coverage_config_path_fd.write(coverage_config) def coverage_cleanup(args): # type: (TestConfig) -> None @@ -54,8 +56,38 @@ def coverage_cleanup(args): # type: (TestConfig) -> None args.coverage_config_base_path = None -def generate_collection_coverage_config(args): # type: (TestConfig) -> str +def generate_coverage_config(args): # type: (TestConfig) -> str """Generate code coverage configuration for tests.""" + if data_context().content.collection: + coverage_config = generate_collection_coverage_config(args) + else: + coverage_config = generate_ansible_coverage_config() + + return coverage_config + + +def generate_ansible_coverage_config(): # type: () -> str + """Generate code coverage configuration for Ansible tests.""" + coverage_config = ''' +[run] +branch = True +concurrency = multiprocessing +parallel = True + +omit = + */python*/dist-packages/* + */python*/site-packages/* + */python*/distutils/* + */pyshared/* + */pytest + */AnsiballZ_*.py +''' + + return coverage_config + + +def generate_collection_coverage_config(args): # type: (TestConfig) -> str + """Generate code coverage configuration for Ansible Collection tests.""" coverage_config = ''' [run] branch = True diff --git a/test/lib/ansible_test/_internal/executor.py b/test/lib/ansible_test/_internal/executor.py index 1618c1527f4..0323d756b16 100644 --- a/test/lib/ansible_test/_internal/executor.py +++ b/test/lib/ansible_test/_internal/executor.py @@ -58,7 +58,7 @@ from .util import ( find_python, get_docker_completion, get_remote_completion, - COVERAGE_OUTPUT_PATH, + COVERAGE_OUTPUT_NAME, cmd_quote, ANSIBLE_ROOT, ANSIBLE_TEST_DATA_ROOT, @@ -985,7 +985,7 @@ def command_integration_filtered(args, targets, all_targets, inventory_path, pre finally: if not args.explain: if args.coverage: - coverage_temp_path = os.path.join(common_temp_path, COVERAGE_OUTPUT_PATH) + coverage_temp_path = os.path.join(common_temp_path, COVERAGE_OUTPUT_NAME) coverage_save_path = 'test/results/coverage' for filename in os.listdir(coverage_temp_path): diff --git a/test/lib/ansible_test/_internal/integration/__init__.py b/test/lib/ansible_test/_internal/integration/__init__.py index 30531209a0a..5f43e2020d9 100644 --- a/test/lib/ansible_test/_internal/integration/__init__.py +++ b/test/lib/ansible_test/_internal/integration/__init__.py @@ -23,8 +23,8 @@ from ..util import ( ApplicationError, display, make_dirs, - COVERAGE_CONFIG_PATH, - COVERAGE_OUTPUT_PATH, + COVERAGE_CONFIG_NAME, + COVERAGE_OUTPUT_NAME, MODE_DIRECTORY, MODE_DIRECTORY_WRITE, MODE_FILE, @@ -37,7 +37,7 @@ from ..util_common import ( ) from ..coverage_util import ( - generate_collection_coverage_config, + generate_coverage_config, ) from ..cache import ( @@ -48,10 +48,6 @@ from ..cloud import ( CloudEnvironmentConfig, ) -from ..data import ( - data_context, -) - def setup_common_temp_dir(args, path): """ @@ -64,22 +60,20 @@ def setup_common_temp_dir(args, path): os.mkdir(path) os.chmod(path, MODE_DIRECTORY) - coverage_config_path = os.path.join(path, COVERAGE_CONFIG_PATH) + if args.coverage: + coverage_config_path = os.path.join(path, COVERAGE_CONFIG_NAME) - if data_context().content.collection: - coverage_config = generate_collection_coverage_config(args) + coverage_config = generate_coverage_config(args) with open(coverage_config_path, 'w') as coverage_config_fd: coverage_config_fd.write(coverage_config) - else: - shutil.copy(os.path.join(ANSIBLE_ROOT, COVERAGE_CONFIG_PATH), coverage_config_path) - os.chmod(coverage_config_path, MODE_FILE) + os.chmod(coverage_config_path, MODE_FILE) - coverage_output_path = os.path.join(path, COVERAGE_OUTPUT_PATH) + coverage_output_path = os.path.join(path, COVERAGE_OUTPUT_NAME) - os.mkdir(coverage_output_path) - os.chmod(coverage_output_path, MODE_DIRECTORY_WRITE) + os.mkdir(coverage_output_path) + os.chmod(coverage_output_path, MODE_DIRECTORY_WRITE) def generate_dependency_map(integration_targets): diff --git a/test/lib/ansible_test/_internal/manage_ci.py b/test/lib/ansible_test/_internal/manage_ci.py index 3b13c36f360..9cde003ed1b 100644 --- a/test/lib/ansible_test/_internal/manage_ci.py +++ b/test/lib/ansible_test/_internal/manage_ci.py @@ -79,7 +79,7 @@ class ManageWindowsCI: for dummy in range(1, 120): try: - intercept_command(self.core_ci.args, cmd, 'ping', env=env) + intercept_command(self.core_ci.args, cmd, 'ping', env=env, disable_coverage=True) return except SubprocessError: time.sleep(10) @@ -171,7 +171,7 @@ class ManageNetworkCI: for dummy in range(1, 90): try: - intercept_command(self.core_ci.args, cmd, 'ping', env=env) + intercept_command(self.core_ci.args, cmd, 'ping', env=env, disable_coverage=True) return except SubprocessError: time.sleep(10) diff --git a/test/lib/ansible_test/_internal/util.py b/test/lib/ansible_test/_internal/util.py index 7c23f156b7f..3b830b5629f 100644 --- a/test/lib/ansible_test/_internal/util.py +++ b/test/lib/ansible_test/_internal/util.py @@ -59,8 +59,8 @@ try: except AttributeError: MAXFD = -1 -COVERAGE_CONFIG_PATH = '.coveragerc' -COVERAGE_OUTPUT_PATH = 'coverage' +COVERAGE_CONFIG_NAME = 'coveragerc' +COVERAGE_OUTPUT_NAME = 'coverage' ANSIBLE_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) ANSIBLE_TEST_ROOT = os.path.join(ANSIBLE_ROOT, 'test', 'lib', 'ansible_test') diff --git a/test/lib/ansible_test/_internal/util_common.py b/test/lib/ansible_test/_internal/util_common.py index 1be296b8855..ed323552d6d 100644 --- a/test/lib/ansible_test/_internal/util_common.py +++ b/test/lib/ansible_test/_internal/util_common.py @@ -11,8 +11,8 @@ import textwrap from .util import ( common_environment, - COVERAGE_CONFIG_PATH, - COVERAGE_OUTPUT_PATH, + COVERAGE_CONFIG_NAME, + COVERAGE_OUTPUT_NAME, display, find_python, ANSIBLE_ROOT, @@ -155,16 +155,22 @@ def get_coverage_environment(args, target_name, version, temp_path, module_cover # config and results are in a temporary directory coverage_config_base_path = temp_path coverage_output_base_path = temp_path - else: + elif args.coverage_config_base_path: # unit tests, sanity tests and other special cases (localhost only) - # config and results are in the source tree - coverage_config_base_path = args.coverage_config_base_path or ANSIBLE_ROOT + # config is in a temporary directory + # results are in the source tree + coverage_config_base_path = args.coverage_config_base_path coverage_output_base_path = os.path.abspath(os.path.join('test/results')) + else: + raise Exception('No temp path and no coverage config base path. Check for missing coverage_context usage.') - config_file = os.path.join(coverage_config_base_path, COVERAGE_CONFIG_PATH) - coverage_file = os.path.join(coverage_output_base_path, COVERAGE_OUTPUT_PATH, '%s=%s=%s=%s=coverage' % ( + config_file = os.path.join(coverage_config_base_path, COVERAGE_CONFIG_NAME) + coverage_file = os.path.join(coverage_output_base_path, COVERAGE_OUTPUT_NAME, '%s=%s=%s=%s=coverage' % ( args.command, target_name, args.coverage_label or 'local-%s' % version, 'python-%s' % version)) + if not args.explain and not os.path.exists(config_file): + raise Exception('Missing coverage config file: %s' % config_file) + if args.coverage_check: # cause the 'coverage' module to be found, but not imported or enabled coverage_file = '' @@ -190,7 +196,7 @@ def get_coverage_environment(args, target_name, version, temp_path, module_cover 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): + virtualenv=None, disable_coverage=False): """ :type args: TestConfig :type cmd: collections.Iterable[str] @@ -203,6 +209,7 @@ def intercept_command(args, cmd, target_name, env, capture=False, data=None, cwd :type temp_path: str | None :type module_coverage: bool :type virtualenv: str | None + :type disable_coverage: bool :rtype: str | None, str | None """ if not env: @@ -223,7 +230,7 @@ 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 args.coverage: + if args.coverage and not disable_coverage: # add the necessary environment variables to enable code coverage collection env.update(get_coverage_environment(args, target_name, version, temp_path, module_coverage))