Coverage for pyEDAA/UCIS/CLI/__init__.py: 77%

99 statements  

« prev     ^ index     » next       coverage.py v7.6.5, created at 2024-11-15 01:09 +0000

1# ==================================================================================================================== # 

2# _____ ____ _ _ _ _ ____ ___ ____ # 

3# _ __ _ _| ____| _ \ / \ / \ | | | |/ ___|_ _/ ___| # 

4# | '_ \| | | | _| | | | |/ _ \ / _ \ | | | | | | |\___ \ # 

5# | |_) | |_| | |___| |_| / ___ \ / ___ \ | |_| | |___ | | ___) | # 

6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)___/ \____|___|____/ # 

7# |_| |___/ # 

8# ==================================================================================================================== # 

9# Authors: # 

10# Patrick Lehmann # 

11# Artur Porebski (Aldec Inc.) # 

12# Michal Pacula (Aldec Inc.) # 

13# # 

14# License: # 

15# ==================================================================================================================== # 

16# Copyright 2021-2022 Electronic Design Automation Abstraction (EDA²) # 

17# # 

18# Licensed under the Apache License, Version 2.0 (the "License"); # 

19# you may not use this file except in compliance with the License. # 

20# You may obtain a copy of the License at # 

21# # 

22# http://www.apache.org/licenses/LICENSE-2.0 # 

23# # 

24# Unless required by applicable law or agreed to in writing, software # 

25# distributed under the License is distributed on an "AS IS" BASIS, # 

26# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

27# See the License for the specific language governing permissions and # 

28# limitations under the License. # 

29# # 

30# SPDX-License-Identifier: Apache-2.0 # 

31# ==================================================================================================================== # 

32# 

33""" 

34Tools to extract data from UCDB files. 

35 

36.. rubric:: Usage 

37 

38First export/convert the Aldec Coverage Database (ACDB) into UCDB (Universal Coverage Database) format. The 

39helper program ``acdb2xml`` (part of Active-HDL or Riviera-PRO installation) can be used. 

40 

41.. code-block:: 

42 

43 acdb2xml -i aggregate.acdb -o ucdb.xml 

44 

45At next use this layer's service program to convert from UCDB to Cobertura format. 

46 

47.. code-block:: 

48 

49 pyedaa-ucis export --ucdb ucdb.xml --cobertura cobertura.xml 

50""" 

51from argparse import RawDescriptionHelpFormatter 

52from pathlib import Path 

53from textwrap import dedent 

54 

55from pyAttributes.ArgParseAttributes import ArgParseMixin, DefaultAttribute, CommandAttribute, ArgumentAttribute, SwitchArgumentAttribute 

56 

57from pyTooling.Decorators import export 

58 

59from pyEDAA.UCIS import __version__, __copyright__, __license__ 

60from pyEDAA.UCIS.UCDB import Parser 

61from pyEDAA.UCIS.Cobertura import CoberturaException 

62 

63 

64@export 

65class ProgramBase(): 

66 """Base-class for all program classes.""" 

67 

68 programTitle = "UCDB Service Program" 

69 

70 def __init__(self) -> None: 

71 pass 

72 

73 def _PrintHeadline(self) -> None: 

74 """Print the programs headline.""" 

75 print("{line}".format(line="=" * 120)) 

76 print("{headline: ^120s}".format(headline=self.programTitle)) 

77 print("{line}".format(line="=" * 120)) 

78 

79 

80@export 

81class Program(ProgramBase, ArgParseMixin): 

82 """Program class to implement the command line interface (CLI) using commands and options.""" 

83 

84 def __init__(self) -> None: 

85 super().__init__() 

86 

87 # Call the constructor of the ArgParseMixin 

88 ArgParseMixin.__init__( 

89 self, 

90 prog="pyedaa-ucis", 

91 description=dedent('''\ 

92 'pyEDAA.UCIS Service Program' to query and transform data to/from UCIS to any other format. 

93 '''), 

94 epilog=dedent("""\ 

95 Currently the following output formats are supported: 

96 * Cobertura (statement coverage - Java oriented format) 

97 """), 

98 formatter_class=RawDescriptionHelpFormatter, 

99 add_help=False 

100 ) 

101 

102# @CommonSwitchArgumentAttribute("-q", "--quiet", dest="quiet", help="Reduce messages to a minimum.") 

103# @CommonSwitchArgumentAttribute("-v", "--verbose", dest="verbose", help="Print out detailed messages.") 

104# @CommonSwitchArgumentAttribute("-d", "--debug", dest="debug", help="Enable debug mode.") 

105 def Run(self) -> None: 

106 ArgParseMixin.Run(self) 

107 

108 @DefaultAttribute() 

109 def HandleDefault(self, _) -> None: 

110 """Handle program calls without any command.""" 

111 self._PrintHeadline() 

112 self._PrintHelp() 

113 

114 @CommandAttribute("help", help="Display help page(s) for the given command name.", description="Display help page(s) for the given command name.") 

115 @ArgumentAttribute(metavar="Command", dest="Command", type=str, nargs="?", help="Print help page(s) for a command.") 

116 def HandleHelp(self, args) -> None: 

117 """Handle program calls with command ``help``.""" 

118 self._PrintHeadline() 

119 self._PrintHelp(args.Command) 

120 

121 @CommandAttribute("version", help="Display version information.", description="Display version information.") 

122 def HandleVersion(self, _) -> None: 

123 """Handle program calls with command ``version``.""" 

124 self._PrintHeadline() 

125 self._PrintVersion() 

126 

127 @CommandAttribute("export", help="Export data from UCDB.", description="Export data from UCDB.") 

128 @ArgumentAttribute("--ucdb", metavar='UCDBFile', dest="ucdb", type=str, help="UCDB file in UCIS format (XML).") 

129 @ArgumentAttribute("--cobertura", metavar='CoberturaFile', dest="cobertura", type=str, help="Cobertura code coverage file (XML).") 

130 @SwitchArgumentAttribute("--merge-instances", dest="mergeInstances", help="Merge statement coverage data for all instances of the same design unit.") 

131 def HandleExport(self, args) -> None: 

132 """Handle program calls with command ``export``.""" 

133 self._PrintHeadline() 

134 

135 returnCode = 0 

136 if args.ucdb is None: 

137 print(f"Option '--ucdb <UCDBFile' is missing.") 

138 returnCode = 3 

139 if args.cobertura is None: 

140 print(f"Option '--cobertura <CoberturaFile' is missing.") 

141 returnCode = 3 

142 

143 if returnCode != 0: 

144 exit(returnCode) 

145 

146 print(f"Exporting code coverage information from UCDB file to Cobertura format ...") 

147 

148 ucdbPath = Path(args.ucdb) 

149 if not ucdbPath.exists(): 149 ↛ 152line 149 didn't jump to line 152 because the condition on line 149 was always true

150 raise FileNotFoundError(f"UCDB databse file '{ucdbPath}' not found.") 

151 

152 coberturaPath = Path(args.cobertura) 

153 

154 print(f" IN -> UCIS (XML): {ucdbPath}") 

155 print(f" OUT <- Cobertura (XML): {coberturaPath}") 

156 

157 parser = Parser(ucdbPath, args.mergeInstances) 

158 model = parser.getCoberturaModel() 

159 

160 with coberturaPath.open('w') as file: 

161 file.write(model.getXml().decode("utf-8")) 

162 

163 print() 

164 

165 try: 

166 lineCoverage = model.linesCovered / model.linesValid * 100 

167 except ZeroDivisionError: 

168 lineCoverage = 100 

169 

170 try: 

171 statementCoverage = parser.statementsCovered / parser.statementsCount * 100 

172 except ZeroDivisionError: 

173 statementCoverage = 100 

174 

175 print(dedent(f"""\ 

176 [DONE] Export and conversion complete. 

177 Line coverage: {lineCoverage} % 

178 Statement coverage: {statementCoverage} % 

179 """) 

180 ) 

181 

182 def _PrintVersion(self): 

183 """Helper function to print the version information.""" 

184 print(dedent(f"""\ 

185 Copyright: {__copyright__} 

186 License: {__license__} 

187 Version: v{__version__} 

188 """) 

189 ) 

190 

191 def _PrintHelp(self, command: str=None): 

192 """Helper function to print the command line parsers help page(s).""" 

193 if (command is None): 

194 self.MainParser.print_help() 

195 elif (command == "help"): 195 ↛ 196line 195 didn't jump to line 196 because the condition on line 195 was never true

196 print("This is a recursion ...") 

197 else: 

198 try: 

199 self.SubParsers[command].print_help() 

200 except KeyError: 

201 print(f"Command {command} is unknown.") 

202 

203 

204@export 

205def main(): 

206 """ 

207 Entrypoint to start program execution. 

208 

209 This function should be called either from: 

210 * ``if __name__ == "__main__":`` or 

211 * ``console_scripts`` entry point configured via ``setuptools`` in ``setup.py``. 

212 

213 This function creates an instance of :class:`Program` in a ``try ... except`` environment. Any exception caught is 

214 formatted and printed before the program returns with a non-zero exit code. 

215 """ 

216 program = Program() 

217 try: 

218 program.Run() 

219 except FileNotFoundError as ex: 

220 print() 

221 print(f"[ERROR] {ex}") 

222 exit(1) 

223 except CoberturaException as ex: 

224 print() 

225 print(f"[INTERNAL ERROR] {ex}") 

226 exit(1) 

227 

228 

229if __name__ == "__main__": 229 ↛ 230line 229 didn't jump to line 230 because the condition on line 229 was never true

230 main()