File: coverage.py

package info (click to toggle)
glean-parser 15.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,260 kB
  • sloc: python: 7,033; ruby: 100; makefile: 87
file content (140 lines) | stat: -rw-r--r-- 4,405 bytes parent folder | download | duplicates (26)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# -*- coding: utf-8 -*-

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

"""
Produce coverage reports from the raw information produced by the
`GLEAN_TEST_COVERAGE` feature.
"""

import json
from .metrics import ObjectTree
from pathlib import Path
import sys
from typing import Any, Dict, List, Optional, Sequence, Set


from . import parser
from . import util


def _outputter_codecovio(metrics: ObjectTree, output_path: Path):
    """
    Output coverage in codecov.io format as defined here:

        https://docs.codecov.io/docs/codecov-custom-coverage-format

    :param metrics: The tree of metrics, already annotated with coverage by
        `_annotate_coverage`.
    :param output_path: The file to output to.
    """
    coverage: Dict[str, List] = {}
    for category in metrics.values():
        for metric in category.values():
            defined_in = metric.defined_in
            if defined_in is not None:
                path = defined_in["filepath"]
                if path not in coverage:
                    with open(path) as fd:
                        nlines = len(list(fd.readlines()))
                    lines = [None] * nlines
                    coverage[path] = lines
                file_section = coverage[path]
                file_section[int(defined_in["line"])] = getattr(metric, "covered", 0)

    with open(output_path, "w") as fd:
        json.dump({"coverage": coverage}, fd)


OUTPUTTERS = {"codecovio": _outputter_codecovio}


def _annotate_coverage(metrics, coverage_entries):
    """
    Annotate each metric with whether it is covered. Sets the attribute
    `covered` to 1 on each metric that is covered.
    """
    mapping = {}
    for category in metrics.values():
        for metric in category.values():
            mapping[metric.identifier()] = metric

    for entry in coverage_entries:
        metric_id = _coverage_entry_to_metric_id(entry)
        if metric_id in mapping:
            mapping[metric_id].covered = 1


def _coverage_entry_to_metric_id(entry: str) -> str:
    """
    Convert a coverage entry to a metric id.

    Technically, the coverage entries are rkv database keys, so are not just
    the metric identifier. This extracts the metric identifier part out.
    """
    # If getting a glean error count, report it as covering the metric the
    # error occurred in, not the `glean.error.*` metric itself.
    if entry.startswith("glean.error."):
        entry = entry.split("/")[-1]
    # If a labeled metric, strip off the label part
    return entry.split("/")[0]


def _read_coverage_entries(coverage_reports: List[Path]) -> Set[str]:
    """
    Read coverage entries from one or more files, and deduplicates them.
    """
    entries = set()

    for coverage_report in coverage_reports:
        with open(coverage_report) as fd:
            for line in fd.readlines():
                entries.add(line.strip())

    return entries


def coverage(
    coverage_reports: List[Path],
    metrics_files: Sequence[Path],
    output_format: str,
    output_file: Path,
    parser_config: Optional[Dict[str, Any]] = None,
    file=sys.stderr,
) -> int:
    """
    Commandline helper for coverage.

    :param coverage_reports: List of coverage report files, output from the
        Glean SDK when the `GLEAN_TEST_COVERAGE` environment variable is set.
    :param metrics_files: List of Path objects to load metrics from.
    :param output_format: The coverage output format to produce. Must be one of
        `OUTPUTTERS.keys()`.
    :param output_file: Path to output coverage report to.
    :param parser_config: Parser configuration object, passed to
      `parser.parse_objects`.
    :return: Non-zero if there were any errors.
    """

    if parser_config is None:
        parser_config = {}

    if output_format not in OUTPUTTERS:
        raise ValueError(f"Unknown outputter {output_format}")

    metrics_files = util.ensure_list(metrics_files)

    all_objects = parser.parse_objects(metrics_files, parser_config)

    if util.report_validation_errors(all_objects):
        return 1

    entries = _read_coverage_entries(coverage_reports)

    _annotate_coverage(all_objects.value, entries)

    OUTPUTTERS[output_format](all_objects.value, output_file)

    return 0