Coverage for lobster/tools/core/ci_report/ci_report.py: 0%
43 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-09-22 04:43 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-09-22 04:43 +0000
1#!/usr/bin/env python3
2#
3# lobster_ci_report - Visualise LOBSTER issues for CI
4# Copyright (C) 2023-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU Affero General Public License as
8# published by the Free Software Foundation, either version 3 of the
9# License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14# Affero General Public License for more details.
15#
16# You should have received a copy of the GNU Affero General Public
17# License along with this program. If not, see
18# <https://www.gnu.org/licenses/>.
20from argparse import ArgumentParser, Namespace
21import os
22from typing import List, Optional, Sequence
24from lobster.common.report import Report
25from lobster.common.items import Tracing_Status
26from lobster.common.meta_data_tool_base import MetaDataToolBase
29def resolve_report_path(lobster_report: str) -> str:
30 # relative paths are given from where `bazel run` was invoked, not
31 # from the runfiles directory the binary actually starts in
32 if os.path.isabs(lobster_report):
33 return lobster_report
34 workdir = os.environ.get("BUILD_WORKING_DIRECTORY")
35 return os.path.join(workdir, lobster_report) if workdir else lobster_report
38def ensure_report_file_exists(
39 argument_parser: ArgumentParser,
40 lobster_report_arg: str,
41 resolved_path: str,
42) -> None:
43 """Exits via argument_parser.error() (SystemExit) IF resolved_path does not
44 name an existing file, distinguishing whether the user gave that path
45 explicitly or whether it is the unmodified default value."""
46 if not os.path.isfile(resolved_path):
47 if lobster_report_arg == "report.lobster":
48 argument_parser.error("specify report file")
49 else:
50 argument_parser.error(f"{lobster_report_arg} is not a file")
53def report_errors_for_untraced_items(report: Report) -> None:
54 """Emits an error (via report.mh) for each message of every item whose
55 tracing status is neither OK nor JUSTIFIED."""
56 for uid in sorted(report.items):
57 item = report.items[uid]
58 if item.tracing_status not in (Tracing_Status.OK,
59 Tracing_Status.JUSTIFIED):
60 for message in item.messages:
61 report.mh.error(item.location,
62 message,
63 fatal = False)
66def format_coverage_lines(report: Report) -> List[str]:
67 """Returns one formatted coverage summary line per report level."""
68 return [
69 f"coverage: {level}: {coverage.coverage:.1f}% "
70 f"({coverage.ok} of {coverage.items} items)"
71 for level, coverage in report.coverage.items()
72 ]
75class CiReportTool(MetaDataToolBase):
76 def __init__(self):
77 super().__init__(
78 name="ci-report",
79 description="Command line tool to check a LOBSTER report",
80 official=True,
81 )
83 self._argument_parser.add_argument(
84 "lobster_report",
85 metavar="file",
86 nargs="?",
87 default="report.lobster",
88 help="Path to the LOBSTER report file (default: report.lobster)",
89 )
90 self._argument_parser.add_argument(
91 "--show-coverage",
92 action="store_true",
93 help="Print the per-level coverage summary of the report.",
94 )
96 def _run_impl(self, options: Namespace) -> int:
97 lobster_report = resolve_report_path(options.lobster_report)
98 ensure_report_file_exists(
99 self._argument_parser, options.lobster_report, lobster_report,
100 )
102 report = Report()
103 report.load_report(lobster_report)
105 report_errors_for_untraced_items(report)
107 if options.show_coverage:
108 for line in format_coverage_lines(report):
109 print(line)
111 if report.mh.errors:
112 return 1
113 return 0
116def main(args: Optional[Sequence[str]] = None) -> int:
117 return CiReportTool().run(args)