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

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 

23import xml.etree.ElementTree as ET 

24 

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 

29 

30 

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) 

44 

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) 

67 

68 else: 

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

70 

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

72 

73 items = [] 

74 

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

108 

109 if source_file in c_files_rel \ 

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

111 

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) 

121 

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" 

130 

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

139 

140 items.append(item) 

141 

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

149 

150 return 0 

151 

152 

153def main() -> int: 

154 return GtestTool().run()