Expand delegation options for coverage commands.
This commit is contained in:
parent
e875e91363
commit
ce04056797
8 changed files with 101 additions and 29 deletions
|
@ -0,0 +1,2 @@
|
|||
minor_changes:
|
||||
- ansible-test - The ``ansible-test coverage`` commands ``combine``, ``report``, ``html`` and ``xml`` now support delegation.
|
|
@ -108,14 +108,17 @@ from .util_common import (
|
|||
|
||||
from .commands.coverage.combine import (
|
||||
command_coverage_combine,
|
||||
CoverageCombineConfig,
|
||||
)
|
||||
|
||||
from .commands.coverage.erase import (
|
||||
command_coverage_erase,
|
||||
CoverageEraseConfig,
|
||||
)
|
||||
|
||||
from .commands.coverage.html import (
|
||||
command_coverage_html,
|
||||
CoverageHtmlConfig,
|
||||
)
|
||||
|
||||
from .commands.coverage.report import (
|
||||
|
@ -125,6 +128,7 @@ from .commands.coverage.report import (
|
|||
|
||||
from .commands.coverage.xml import (
|
||||
command_coverage_xml,
|
||||
CoverageXmlConfig,
|
||||
)
|
||||
|
||||
from .commands.coverage.analyze.targets.generate import (
|
||||
|
@ -154,7 +158,6 @@ from .commands.coverage.analyze.targets.missing import (
|
|||
|
||||
from .commands.coverage import (
|
||||
COVERAGE_GROUPS,
|
||||
CoverageConfig,
|
||||
)
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
|
@ -579,6 +582,10 @@ def parse_args():
|
|||
|
||||
add_environments(coverage_common, argparse, isolated_delegation=False)
|
||||
|
||||
coverage_common_isolated_delegation = argparse.ArgumentParser(add_help=False, parents=[common])
|
||||
|
||||
add_environments(coverage_common_isolated_delegation, argparse)
|
||||
|
||||
coverage = subparsers.add_parser('coverage',
|
||||
help='code coverage management and reporting')
|
||||
|
||||
|
@ -588,11 +595,11 @@ def parse_args():
|
|||
add_coverage_analyze(coverage_subparsers, coverage_common)
|
||||
|
||||
coverage_combine = coverage_subparsers.add_parser('combine',
|
||||
parents=[coverage_common],
|
||||
parents=[coverage_common_isolated_delegation],
|
||||
help='combine coverage data and rewrite remote paths')
|
||||
|
||||
coverage_combine.set_defaults(func=command_coverage_combine,
|
||||
config=CoverageConfig)
|
||||
config=CoverageCombineConfig)
|
||||
|
||||
coverage_combine.add_argument('--export',
|
||||
help='directory to export combined coverage files to')
|
||||
|
@ -604,10 +611,10 @@ def parse_args():
|
|||
help='erase coverage data files')
|
||||
|
||||
coverage_erase.set_defaults(func=command_coverage_erase,
|
||||
config=CoverageConfig)
|
||||
config=CoverageEraseConfig)
|
||||
|
||||
coverage_report = coverage_subparsers.add_parser('report',
|
||||
parents=[coverage_common],
|
||||
parents=[coverage_common_isolated_delegation],
|
||||
help='generate console coverage report')
|
||||
|
||||
coverage_report.set_defaults(func=command_coverage_report,
|
||||
|
@ -629,20 +636,20 @@ def parse_args():
|
|||
add_extra_coverage_options(coverage_report)
|
||||
|
||||
coverage_html = coverage_subparsers.add_parser('html',
|
||||
parents=[coverage_common],
|
||||
parents=[coverage_common_isolated_delegation],
|
||||
help='generate html coverage report')
|
||||
|
||||
coverage_html.set_defaults(func=command_coverage_html,
|
||||
config=CoverageConfig)
|
||||
config=CoverageHtmlConfig)
|
||||
|
||||
add_extra_coverage_options(coverage_html)
|
||||
|
||||
coverage_xml = coverage_subparsers.add_parser('xml',
|
||||
parents=[coverage_common],
|
||||
parents=[coverage_common_isolated_delegation],
|
||||
help='generate xml coverage report')
|
||||
|
||||
coverage_xml.set_defaults(func=command_coverage_xml,
|
||||
config=CoverageConfig)
|
||||
config=CoverageXmlConfig)
|
||||
|
||||
add_extra_coverage_options(coverage_xml)
|
||||
|
||||
|
|
|
@ -59,12 +59,6 @@ class CoverageConfig(EnvironmentConfig):
|
|||
def __init__(self, args): # type: (t.Any) -> None
|
||||
super(CoverageConfig, self).__init__(args, 'coverage')
|
||||
|
||||
self.group_by = frozenset(args.group_by) if 'group_by' in args and args.group_by else set() # type: t.FrozenSet[str]
|
||||
self.all = args.all if 'all' in args else False # type: bool
|
||||
self.stub = args.stub if 'stub' in args else False # type: bool
|
||||
self.export = args.export if 'export' in args else None # type: str
|
||||
self.coverage = False # temporary work-around to support intercept_command in cover.py
|
||||
|
||||
|
||||
def initialize_coverage(args): # type: (CoverageConfig) -> coverage_module
|
||||
"""Delegate execution if requested, install requirements, then import and return the coverage module. Raises an exception if coverage is not available."""
|
||||
|
@ -111,6 +105,11 @@ def run_coverage(args, output_file, command, cmd): # type: (CoverageConfig, str
|
|||
intercept_command(args, target_name='coverage', env=env, cmd=cmd, disable_coverage=True)
|
||||
|
||||
|
||||
def get_all_coverage_files(): # type: () -> t.List[str]
|
||||
"""Return a list of all coverage file paths."""
|
||||
return get_python_coverage_files() + get_powershell_coverage_files()
|
||||
|
||||
|
||||
def get_python_coverage_files(path=None): # type: (t.Optional[str]) -> t.List[str]
|
||||
"""Return the list of Python coverage file paths."""
|
||||
return get_coverage_files('python', path)
|
||||
|
|
|
@ -5,6 +5,8 @@ __metaclass__ = type
|
|||
import os
|
||||
import json
|
||||
|
||||
from ... import types as t
|
||||
|
||||
from ...target import (
|
||||
walk_compile_targets,
|
||||
walk_powershell_targets,
|
||||
|
@ -17,7 +19,7 @@ from ...io import (
|
|||
from ...util import (
|
||||
ANSIBLE_TEST_DATA_ROOT,
|
||||
display,
|
||||
find_executable,
|
||||
ApplicationError,
|
||||
)
|
||||
|
||||
from ...util_common import (
|
||||
|
@ -27,10 +29,19 @@ from ...util_common import (
|
|||
write_json_test_results,
|
||||
)
|
||||
|
||||
from ...executor import (
|
||||
Delegate,
|
||||
)
|
||||
|
||||
from ...data import (
|
||||
data_context,
|
||||
)
|
||||
|
||||
from . import (
|
||||
enumerate_python_arcs,
|
||||
enumerate_powershell_lines,
|
||||
get_collection_path_regexes,
|
||||
get_all_coverage_files,
|
||||
get_python_coverage_files,
|
||||
get_python_modules,
|
||||
get_powershell_coverage_files,
|
||||
|
@ -44,9 +55,28 @@ from . import (
|
|||
|
||||
def command_coverage_combine(args):
|
||||
"""Patch paths in coverage files and merge into a single file.
|
||||
:type args: CoverageConfig
|
||||
:type args: CoverageCombineConfig
|
||||
:rtype: list[str]
|
||||
"""
|
||||
if args.delegate:
|
||||
if args.docker or args.remote:
|
||||
paths = get_all_coverage_files()
|
||||
exported_paths = [path for path in paths if path.endswith('=coverage.combined')]
|
||||
|
||||
if not exported_paths:
|
||||
raise ExportedCoverageDataNotFound()
|
||||
|
||||
pairs = [(path, os.path.relpath(path, data_context().content.root)) for path in exported_paths]
|
||||
|
||||
def coverage_callback(files): # type: (t.List[t.Tuple[str, str]]) -> None
|
||||
"""Add the coverage files to the payload file list."""
|
||||
display.info('Including %d exported coverage file(s) in payload.' % len(pairs), verbosity=1)
|
||||
files.extend(pairs)
|
||||
|
||||
data_context().register_payload_callback(coverage_callback)
|
||||
|
||||
raise Delegate()
|
||||
|
||||
paths = _command_coverage_combine_powershell(args) + _command_coverage_combine_python(args)
|
||||
|
||||
for path in paths:
|
||||
|
@ -55,9 +85,18 @@ def command_coverage_combine(args):
|
|||
return paths
|
||||
|
||||
|
||||
class ExportedCoverageDataNotFound(ApplicationError):
|
||||
"""Exception when no combined coverage data is present yet is required."""
|
||||
def __init__(self):
|
||||
super(ExportedCoverageDataNotFound, self).__init__(
|
||||
'Coverage data must be exported before processing with the `--docker` or `--remote` option.\n'
|
||||
'Export coverage with `ansible-test coverage combine` using the `--export` option.\n'
|
||||
'The exported files must be in the directory: %s/' % ResultType.COVERAGE.relative_path)
|
||||
|
||||
|
||||
def _command_coverage_combine_python(args):
|
||||
"""
|
||||
:type args: CoverageConfig
|
||||
:type args: CoverageCombineConfig
|
||||
:rtype: list[str]
|
||||
"""
|
||||
coverage = initialize_coverage(args)
|
||||
|
@ -136,7 +175,7 @@ def _command_coverage_combine_python(args):
|
|||
|
||||
def _command_coverage_combine_powershell(args):
|
||||
"""
|
||||
:type args: CoverageConfig
|
||||
:type args: CoverageCombineConfig
|
||||
:rtype: list[str]
|
||||
"""
|
||||
coverage_files = get_powershell_coverage_files()
|
||||
|
@ -217,7 +256,7 @@ def _command_coverage_combine_powershell(args):
|
|||
|
||||
def _get_coverage_targets(args, walk_func):
|
||||
"""
|
||||
:type args: CoverageConfig
|
||||
:type args: CoverageCombineConfig
|
||||
:type walk_func: Func
|
||||
:rtype: list[tuple[str, int]]
|
||||
"""
|
||||
|
@ -240,7 +279,7 @@ def _get_coverage_targets(args, walk_func):
|
|||
|
||||
def _build_stub_groups(args, sources, default_stub_value):
|
||||
"""
|
||||
:type args: CoverageConfig
|
||||
:type args: CoverageCombineConfig
|
||||
:type sources: List[tuple[str, int]]
|
||||
:type default_stub_value: Func[List[str]]
|
||||
:rtype: dict
|
||||
|
@ -273,7 +312,7 @@ def _build_stub_groups(args, sources, default_stub_value):
|
|||
|
||||
def get_coverage_group(args, coverage_file):
|
||||
"""
|
||||
:type args: CoverageConfig
|
||||
:type args: CoverageCombineConfig
|
||||
:type coverage_file: str
|
||||
:rtype: str
|
||||
"""
|
||||
|
@ -306,3 +345,16 @@ def get_coverage_group(args, coverage_file):
|
|||
group = group.lstrip('=')
|
||||
|
||||
return group
|
||||
|
||||
|
||||
class CoverageCombineConfig(CoverageConfig):
|
||||
"""Configuration for the coverage combine command."""
|
||||
def __init__(self, args): # type: (t.Any) -> None
|
||||
super(CoverageCombineConfig, self).__init__(args)
|
||||
|
||||
self.group_by = frozenset(args.group_by) if args.group_by else frozenset() # type: t.FrozenSet[str]
|
||||
self.all = args.all # type: bool
|
||||
self.stub = args.stub # type: bool
|
||||
|
||||
# only available to coverage combine
|
||||
self.export = args.export if 'export' in args else False # type: str
|
||||
|
|
|
@ -13,7 +13,7 @@ from . import (
|
|||
)
|
||||
|
||||
|
||||
def command_coverage_erase(args): # type: (CoverageConfig) -> None
|
||||
def command_coverage_erase(args): # type: (CoverageEraseConfig) -> None
|
||||
"""Erase code coverage data files collected during test runs."""
|
||||
coverage_dir = ResultType.COVERAGE.path
|
||||
|
||||
|
@ -25,3 +25,7 @@ def command_coverage_erase(args): # type: (CoverageConfig) -> None
|
|||
|
||||
if not args.explain:
|
||||
os.remove(path)
|
||||
|
||||
|
||||
class CoverageEraseConfig(CoverageConfig):
|
||||
"""Configuration for the coverage erase command."""
|
||||
|
|
|
@ -18,17 +18,17 @@ from ...util_common import (
|
|||
|
||||
from .combine import (
|
||||
command_coverage_combine,
|
||||
CoverageCombineConfig,
|
||||
)
|
||||
|
||||
from . import (
|
||||
run_coverage,
|
||||
CoverageConfig,
|
||||
)
|
||||
|
||||
|
||||
def command_coverage_html(args):
|
||||
"""
|
||||
:type args: CoverageConfig
|
||||
:type args: CoverageHtmlConfig
|
||||
"""
|
||||
output_files = command_coverage_combine(args)
|
||||
|
||||
|
@ -43,3 +43,7 @@ def command_coverage_html(args):
|
|||
run_coverage(args, output_file, 'html', ['-i', '-d', dir_name])
|
||||
|
||||
display.info('HTML report generated: file:///%s' % os.path.join(dir_name, 'index.html'))
|
||||
|
||||
|
||||
class CoverageHtmlConfig(CoverageCombineConfig):
|
||||
"""Configuration for the coverage html command."""
|
||||
|
|
|
@ -18,11 +18,11 @@ from ...data import (
|
|||
|
||||
from .combine import (
|
||||
command_coverage_combine,
|
||||
CoverageCombineConfig,
|
||||
)
|
||||
|
||||
from . import (
|
||||
run_coverage,
|
||||
CoverageConfig,
|
||||
)
|
||||
|
||||
|
||||
|
@ -143,7 +143,7 @@ def _generate_powershell_output_report(args, coverage_file):
|
|||
return report
|
||||
|
||||
|
||||
class CoverageReportConfig(CoverageConfig):
|
||||
class CoverageReportConfig(CoverageCombineConfig):
|
||||
"""Configuration for the coverage report command."""
|
||||
def __init__(self, args):
|
||||
"""
|
||||
|
|
|
@ -36,17 +36,17 @@ from ...data import (
|
|||
|
||||
from .combine import (
|
||||
command_coverage_combine,
|
||||
CoverageCombineConfig,
|
||||
)
|
||||
|
||||
from . import (
|
||||
run_coverage,
|
||||
CoverageConfig,
|
||||
)
|
||||
|
||||
|
||||
def command_coverage_xml(args):
|
||||
"""
|
||||
:type args: CoverageConfig
|
||||
:type args: CoverageXmlConfig
|
||||
"""
|
||||
output_files = command_coverage_combine(args)
|
||||
|
||||
|
@ -189,3 +189,7 @@ def _add_cobertura_package(packages, package_name, package_data):
|
|||
})
|
||||
|
||||
return total_lines_hit, total_line_count
|
||||
|
||||
|
||||
class CoverageXmlConfig(CoverageCombineConfig):
|
||||
"""Configuration for the coverage xml command."""
|
||||
|
|
Loading…
Reference in a new issue