diff --git a/test/lib/ansible_test/_internal/coverage/__init__.py b/test/lib/ansible_test/_internal/coverage/__init__.py index db05a7cdb27..dcf0ba196f7 100644 --- a/test/lib/ansible_test/_internal/coverage/__init__.py +++ b/test/lib/ansible_test/_internal/coverage/__init__.py @@ -52,6 +52,17 @@ COVERAGE_CONFIG_PATH = os.path.join(ANSIBLE_TEST_DATA_ROOT, 'coveragerc') COVERAGE_OUTPUT_FILE_NAME = 'coverage' +class CoverageConfig(EnvironmentConfig): + """Configuration for the coverage command.""" + 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.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.""" if args.delegate: @@ -81,19 +92,19 @@ 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_python_coverage_files(): # type: () -> t.List[str] +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') + return get_coverage_files('python', path) -def get_powershell_coverage_files(): # type: () -> t.List[str] +def get_powershell_coverage_files(path=None): # type: (t.Optional[str]) -> t.List[str] """Return the list of PowerShell coverage file paths.""" - return get_coverage_files('powershell') + return get_coverage_files('powershell', path) -def get_coverage_files(language): # type: (str) -> t.List[str] +def get_coverage_files(language, path=None): # type: (str, t.Optional[str]) -> t.List[str] """Return the list of coverage file paths for the given language.""" - coverage_dir = ResultType.COVERAGE.path + coverage_dir = path or ResultType.COVERAGE.path coverage_files = [os.path.join(coverage_dir, f) for f in os.listdir(coverage_dir) if '=coverage.' in f and '=%s' % language in f] @@ -247,17 +258,6 @@ def sanitize_filename( return filename -class CoverageConfig(EnvironmentConfig): - """Configuration for the coverage command.""" - 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.coverage = False # temporary work-around to support intercept_command in cover.py - - class PathChecker: """Checks code coverage paths to verify they are valid and reports on the findings.""" def __init__(self, args, collection_search_re=None): # type: (CoverageConfig, t.Optional[t.Pattern]) -> None diff --git a/test/lib/ansible_test/_internal/coverage/analyze/targets/__init__.py b/test/lib/ansible_test/_internal/coverage/analyze/targets/__init__.py index 349f47ab94e..8fe571b8777 100644 --- a/test/lib/ansible_test/_internal/coverage/analyze/targets/__init__.py +++ b/test/lib/ansible_test/_internal/coverage/analyze/targets/__init__.py @@ -30,6 +30,14 @@ if t.TYPE_CHECKING: TargetSetIndexes = t.Dict[t.FrozenSet[int], int] +class CoverageAnalyzeTargetsConfig(CoverageAnalyzeConfig): + """Configuration for the `coverage analyze targets` command.""" + def __init__(self, args): # type: (t.Any) -> None + super(CoverageAnalyzeTargetsConfig, self).__init__(args) + + self.info_stderr = True + + def make_report(target_indexes, arcs, lines): # type: (TargetIndexes, Arcs, Lines) -> t.Dict[str, t.Any] """Condense target indexes, arcs and lines into a compact report.""" set_indexes = {} @@ -144,11 +152,3 @@ def generate_indexes(target_indexes, data): # type: (TargetIndexes, NamedPoints result_point.add(get_target_index(target_name, target_indexes)) return results - - -class CoverageAnalyzeTargetsConfig(CoverageAnalyzeConfig): - """Configuration for the `coverage analyze targets` command.""" - def __init__(self, args): # type: (t.Any) -> None - super(CoverageAnalyzeTargetsConfig, self).__init__(args) - - self.info_stderr = True diff --git a/test/lib/ansible_test/_internal/coverage/analyze/targets/combine.py b/test/lib/ansible_test/_internal/coverage/analyze/targets/combine.py index 33526379baa..35148ff6ad6 100644 --- a/test/lib/ansible_test/_internal/coverage/analyze/targets/combine.py +++ b/test/lib/ansible_test/_internal/coverage/analyze/targets/combine.py @@ -21,6 +21,15 @@ if t.TYPE_CHECKING: ) +class CoverageAnalyzeTargetsCombineConfig(CoverageAnalyzeTargetsConfig): + """Configuration for the `coverage analyze targets combine` command.""" + def __init__(self, args): # type: (t.Any) -> None + super(CoverageAnalyzeTargetsCombineConfig, self).__init__(args) + + self.input_files = args.input_file # type: t.List[str] + self.output_file = args.output_file # type: str + + def command_coverage_analyze_targets_combine(args): # type: (CoverageAnalyzeTargetsCombineConfig) -> None """Combine integration test target code coverage reports.""" combined_target_indexes = {} # type: TargetIndexes @@ -53,12 +62,3 @@ def merge_indexes( for covered_target_index in covered_target_indexes: combined_point.add(get_target_index(source_index[covered_target_index], combined_index)) - - -class CoverageAnalyzeTargetsCombineConfig(CoverageAnalyzeTargetsConfig): - """Configuration for the `coverage analyze targets combine` command.""" - def __init__(self, args): # type: (t.Any) -> None - super(CoverageAnalyzeTargetsCombineConfig, self).__init__(args) - - self.input_files = args.input_file # type: t.List[str] - self.output_file = args.output_file # type: str diff --git a/test/lib/ansible_test/_internal/coverage/analyze/targets/expand.py b/test/lib/ansible_test/_internal/coverage/analyze/targets/expand.py index db054a8ed33..388dd6cb6dd 100644 --- a/test/lib/ansible_test/_internal/coverage/analyze/targets/expand.py +++ b/test/lib/ansible_test/_internal/coverage/analyze/targets/expand.py @@ -17,6 +17,15 @@ from . import ( ) +class CoverageAnalyzeTargetsExpandConfig(CoverageAnalyzeTargetsConfig): + """Configuration for the `coverage analyze targets expand` command.""" + def __init__(self, args): # type: (t.Any) -> None + super(CoverageAnalyzeTargetsExpandConfig, self).__init__(args) + + self.input_file = args.input_file # type: str + self.output_file = args.output_file # type: str + + def command_coverage_analyze_targets_expand(args): # type: (CoverageAnalyzeTargetsExpandConfig) -> None """Expand target names in an aggregated coverage file.""" covered_targets, covered_path_arcs, covered_path_lines = read_report(args.input_file) @@ -28,12 +37,3 @@ def command_coverage_analyze_targets_expand(args): # type: (CoverageAnalyzeTarg if not args.explain: write_json_file(args.output_file, report, encoder=SortedSetEncoder) - - -class CoverageAnalyzeTargetsExpandConfig(CoverageAnalyzeTargetsConfig): - """Configuration for the `coverage analyze targets expand` command.""" - def __init__(self, args): # type: (t.Any) -> None - super(CoverageAnalyzeTargetsExpandConfig, self).__init__(args) - - self.input_file = args.input_file # type: str - self.output_file = args.output_file # type: str diff --git a/test/lib/ansible_test/_internal/coverage/analyze/targets/filter.py b/test/lib/ansible_test/_internal/coverage/analyze/targets/filter.py index 95bec3760bc..e90fb2277e6 100644 --- a/test/lib/ansible_test/_internal/coverage/analyze/targets/filter.py +++ b/test/lib/ansible_test/_internal/coverage/analyze/targets/filter.py @@ -22,6 +22,19 @@ if t.TYPE_CHECKING: ) +class CoverageAnalyzeTargetsFilterConfig(CoverageAnalyzeTargetsConfig): + """Configuration for the `coverage analyze targets filter` command.""" + def __init__(self, args): # type: (t.Any) -> None + super(CoverageAnalyzeTargetsFilterConfig, self).__init__(args) + + self.input_file = args.input_file # type: str + self.output_file = args.output_file # type: str + self.include_targets = args.include_targets # type: t.List[str] + self.exclude_targets = args.exclude_targets # type: t.List[str] + self.include_path = args.include_path # type: t.Optional[str] + self.exclude_path = args.exclude_path # type: t.Optional[str] + + def command_coverage_analyze_targets_filter(args): # type: (CoverageAnalyzeTargetsFilterConfig) -> None """Filter target names in an aggregated coverage file.""" covered_targets, covered_path_arcs, covered_path_lines = read_report(args.input_file) @@ -89,16 +102,3 @@ def filter_data( result[src_path] = dst_points return result - - -class CoverageAnalyzeTargetsFilterConfig(CoverageAnalyzeTargetsConfig): - """Configuration for the `coverage analyze targets filter` command.""" - def __init__(self, args): # type: (t.Any) -> None - super(CoverageAnalyzeTargetsFilterConfig, self).__init__(args) - - self.input_file = args.input_file # type: str - self.output_file = args.output_file # type: str - self.include_targets = args.include_targets # type: t.List[str] - self.exclude_targets = args.exclude_targets # type: t.List[str] - self.include_path = args.include_path # type: t.Optional[str] - self.exclude_path = args.exclude_path # type: t.Optional[str] diff --git a/test/lib/ansible_test/_internal/coverage/analyze/targets/generate.py b/test/lib/ansible_test/_internal/coverage/analyze/targets/generate.py index 3ff074b2427..a14b6f55993 100644 --- a/test/lib/ansible_test/_internal/coverage/analyze/targets/generate.py +++ b/test/lib/ansible_test/_internal/coverage/analyze/targets/generate.py @@ -44,25 +44,35 @@ if t.TYPE_CHECKING: ) +class CoverageAnalyzeTargetsGenerateConfig(CoverageAnalyzeTargetsConfig): + """Configuration for the `coverage analyze targets generate` command.""" + def __init__(self, args): # type: (t.Any) -> None + super(CoverageAnalyzeTargetsGenerateConfig, self).__init__(args) + + self.input_dir = args.input_dir or ResultType.COVERAGE.path # type: str + self.output_file = args.output_file # type: str + + def command_coverage_analyze_targets_generate(args): # type: (CoverageAnalyzeTargetsGenerateConfig) -> None """Analyze code coverage data to determine which integration test targets provide coverage for each arc or line.""" root = data_context().content.root target_indexes = {} - arcs = dict((os.path.relpath(path, root), data) for path, data in analyze_python_coverage(args, target_indexes).items()) - lines = dict((os.path.relpath(path, root), data) for path, data in analyze_powershell_coverage(args, target_indexes).items()) + arcs = dict((os.path.relpath(path, root), data) for path, data in analyze_python_coverage(args, args.input_dir, target_indexes).items()) + lines = dict((os.path.relpath(path, root), data) for path, data in analyze_powershell_coverage(args, args.input_dir, target_indexes).items()) report = make_report(target_indexes, arcs, lines) write_report(args, report, args.output_file) def analyze_python_coverage( - args, # type: CoverageAnalyzeTargetsConfig + args, # type: CoverageAnalyzeTargetsGenerateConfig + path, # type: str target_indexes, # type: TargetIndexes ): # type: (...) -> Arcs """Analyze Python code coverage.""" results = {} # type: Arcs collection_search_re, collection_sub_re = get_collection_path_regexes() modules = get_python_modules() - python_files = get_python_coverage_files() + python_files = get_python_coverage_files(path) coverage = initialize_coverage(args) for python_file in python_files: @@ -85,13 +95,14 @@ def analyze_python_coverage( def analyze_powershell_coverage( - args, # type: CoverageAnalyzeTargetsConfig + args, # type: CoverageAnalyzeTargetsGenerateConfig + path, # type: str target_indexes, # type: TargetIndexes ): # type: (...) -> Lines """Analyze PowerShell code coverage""" results = {} # type: Lines collection_search_re, collection_sub_re = get_collection_path_regexes() - powershell_files = get_powershell_coverage_files() + powershell_files = get_powershell_coverage_files(path) for powershell_file in powershell_files: if not is_integration_coverage_file(powershell_file): @@ -113,7 +124,7 @@ def analyze_powershell_coverage( def prune_invalid_filenames( - args, # type: CoverageAnalyzeTargetsConfig + args, # type: CoverageAnalyzeTargetsGenerateConfig results, # type: t.Dict[str, t.Any] collection_search_re=None, # type: t.Optional[str] ): # type: (...) -> None @@ -133,12 +144,3 @@ def get_target_name(path): # type: (str) -> str def is_integration_coverage_file(path): # type: (str) -> bool """Returns True if the coverage file came from integration tests, otherwise False.""" return os.path.basename(path).split('=')[0] in ('integration', 'windows-integration', 'network-integration') - - -class CoverageAnalyzeTargetsGenerateConfig(CoverageAnalyzeTargetsConfig): - """Configuration for the `coverage analyze targets generate` command.""" - def __init__(self, args): # type: (t.Any) -> None - super(CoverageAnalyzeTargetsGenerateConfig, self).__init__(args) - - self.input_dir = args.input_dir or ResultType.COVERAGE.path # type: str - self.output_file = args.output_file # type: str diff --git a/test/lib/ansible_test/_internal/coverage/analyze/targets/missing.py b/test/lib/ansible_test/_internal/coverage/analyze/targets/missing.py index eace9b26d36..613a0ef275d 100644 --- a/test/lib/ansible_test/_internal/coverage/analyze/targets/missing.py +++ b/test/lib/ansible_test/_internal/coverage/analyze/targets/missing.py @@ -25,6 +25,19 @@ if t.TYPE_CHECKING: ) +class CoverageAnalyzeTargetsMissingConfig(CoverageAnalyzeTargetsConfig): + """Configuration for the `coverage analyze targets missing` command.""" + def __init__(self, args): # type: (t.Any) -> None + super(CoverageAnalyzeTargetsMissingConfig, self).__init__(args) + + self.from_file = args.from_file # type: str + self.to_file = args.to_file # type: str + self.output_file = args.output_file # type: str + + self.only_gaps = args.only_gaps # type: bool + self.only_exists = args.only_exists # type: bool + + def command_coverage_analyze_targets_missing(args): # type: (CoverageAnalyzeTargetsMissingConfig) -> None """Identify aggregated coverage in one file missing from another.""" from_targets, from_path_arcs, from_path_lines = read_report(args.from_file) @@ -94,16 +107,3 @@ def find_missing( target_index.update(get_target_index(name, target_indexes) for name in remaining_targets) return target_data - - -class CoverageAnalyzeTargetsMissingConfig(CoverageAnalyzeTargetsConfig): - """Configuration for the `coverage analyze targets missing` command.""" - def __init__(self, args): # type: (t.Any) -> None - super(CoverageAnalyzeTargetsMissingConfig, self).__init__(args) - - self.from_file = args.from_file # type: str - self.to_file = args.to_file # type: str - self.output_file = args.output_file # type: str - - self.only_gaps = args.only_gaps # type: bool - self.only_exists = args.only_exists # type: bool