Coverage for lobster/tools/gtest/gtest.py: 0%
92 statements
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-27 13:02 +0000
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-27 13:02 +0000
1#!/usr/bin/env python3
2#
3# lobster_gtest - Extract GoogleTest tracing tags for LOBSTER
4# Copyright (C) 2022-2024 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 Namespace
21import sys
22import os.path
23from typing import Optional, Sequence
24import xml.etree.ElementTree as ET
26from lobster.common.items import Tracing_Tag, Activity
27from lobster.common.location import Void_Reference, File_Reference
28from lobster.common.io import lobster_write
29from lobster.common.meta_data_tool_base import MetaDataToolBase
32class GtestTool(MetaDataToolBase):
33 def __init__(self):
34 super().__init__(
35 name = "gtest",
36 description = "Extract tracing tags from GoogleTest XML output",
37 official = True,
38 )
39 self._argument_parser.add_argument(
40 "files",
41 nargs="+",
42 metavar="FILE|DIR",
43 )
44 self._argument_parser.add_argument("--out", default=None)
46 def _run_impl(self, options: Namespace) -> int:
47 c_files_rel = {}
48 file_list = []
49 for item in options.files:
50 if os.path.isfile(item):
51 file_list.append(item)
52 elif os.path.isdir(item):
53 for path, _, files in os.walk(item, followlinks=True):
54 for filename in files:
55 if not os.path.isfile(os.path.join(path, filename)):
56 continue
57 _, ext = os.path.splitext(filename)
58 if ext in (".xml", ):
59 file_list.append(os.path.join(path, filename))
60 elif ext in (".cpp", ".cc", ".c"):
61 fullname = os.path.relpath(
62 os.path.realpath(os.path.join(path, filename)))
63 if ".cache" in str(fullname):
64 continue
65 if filename not in c_files_rel:
66 c_files_rel[filename] = set()
67 c_files_rel[filename].add(fullname)
69 else:
70 self._argument_parser.error("%s is not a file or directory" % item)
72 file_list = {os.path.realpath(os.path.abspath(f)) for f in file_list}
74 items = []
76 for filename in file_list:
77 tree = ET.parse(filename)
78 root = tree.getroot()
79 if root.tag != "testsuites":
80 continue
81 for suite in root:
82 assert suite.tag == "testsuite"
83 suite_name = suite.attrib["name"]
84 for testcase in suite:
85 if testcase.tag != "testcase":
86 continue
87 test_name = testcase.attrib["name"]
88 test_executed = testcase.attrib["status"] == "run"
89 test_ok = True
90 test_tags = []
91 source_file = testcase.attrib.get("file", None)
92 source_line = int(testcase.attrib["line"]) \
93 if "line" in testcase.attrib \
94 else None
95 for props in testcase:
96 if props.tag == "failure":
97 test_ok = False
98 elif props.tag == "properties":
99 for prop in props:
100 assert prop.tag == "property"
101 if prop.attrib["name"] == "lobster-tracing":
102 test_tags += [
103 x.strip()
104 for x in prop.attrib["value"].split(",")]
105 elif prop.attrib["name"] == "lobster-tracing-file":
106 source_file = prop.attrib["value"]
107 elif prop.attrib["name"] == "lobster-tracing-line":
108 source_line = int(prop.attrib["value"])
110 if source_file in c_files_rel \
111 and (len(c_files_rel[source_file]) == 1):
113 test_source = File_Reference(
114 filename = list(c_files_rel[source_file])[0],
115 line = source_line)
116 elif source_file is None:
117 test_source = Void_Reference()
118 else:
119 test_source = File_Reference(
120 filename = source_file,
121 line = source_line)
123 uid = "%s:%s" % (suite_name, test_name)
124 if test_executed:
125 if test_ok:
126 status = "ok"
127 else:
128 status = "fail"
129 else:
130 status = "not run"
132 tag = Tracing_Tag("gtest", uid)
133 item = Activity(tag = tag,
134 location = test_source,
135 framework = "GoogleTest",
136 kind = "test",
137 status = status)
138 for ref in test_tags:
139 item.add_tracing_target(Tracing_Tag("req", ref))
141 items.append(item)
143 if options.out:
144 with open(options.out, "w", encoding="UTF-8") as fd:
145 lobster_write(fd, Activity, "lobster_gtest", items)
146 print(f"Written output for {len(items)} items to {options.out}")
147 else:
148 lobster_write(sys.stdout, Activity, "lobster_gtest", items)
149 print()
151 return 0
154def main(args: Optional[Sequence[str]] = None) -> int:
155 return GtestTool().run(args)