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

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/>. 

19 

20from argparse import Namespace 

21import sys 

22import os.path 

23from typing import Optional, Sequence 

24import xml.etree.ElementTree as ET 

25 

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 

30 

31 

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) 

45 

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) 

68 

69 else: 

70 self._argument_parser.error("%s is not a file or directory" % item) 

71 

72 file_list = {os.path.realpath(os.path.abspath(f)) for f in file_list} 

73 

74 items = [] 

75 

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"]) 

109 

110 if source_file in c_files_rel \ 

111 and (len(c_files_rel[source_file]) == 1): 

112 

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) 

122 

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" 

131 

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)) 

140 

141 items.append(item) 

142 

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() 

150 

151 return 0 

152 

153 

154def main(args: Optional[Sequence[str]] = None) -> int: 

155 return GtestTool().run(args)