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