Coverage for pyEDAA/UCIS/CLI/__init__.py: 77%
99 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 01:05 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 01:05 +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.
36.. rubric:: Usage
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.
41.. code-block::
43 acdb2xml -i aggregate.acdb -o ucdb.xml
45At next use this layer's service program to convert from UCDB to Cobertura format.
47.. code-block::
49 pyedaa-ucis export --ucdb ucdb.xml --cobertura cobertura.xml
50"""
51from argparse import RawDescriptionHelpFormatter
52from pathlib import Path
53from textwrap import dedent
55from pyAttributes.ArgParseAttributes import ArgParseMixin, DefaultAttribute, CommandAttribute, ArgumentAttribute, SwitchArgumentAttribute
57from pyTooling.Decorators import export
59from pyEDAA.UCIS import __version__, __copyright__, __license__
60from pyEDAA.UCIS.UCDB import Parser
61from pyEDAA.UCIS.Cobertura import CoberturaException
64@export
65class ProgramBase():
66 """Base-class for all program classes."""
68 programTitle = "UCDB Service Program"
70 def __init__(self) -> None:
71 pass
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))
80@export
81class Program(ProgramBase, ArgParseMixin):
82 """Program class to implement the command line interface (CLI) using commands and options."""
84 def __init__(self) -> None:
85 super().__init__()
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 )
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)
108 @DefaultAttribute()
109 def HandleDefault(self, _) -> None:
110 """Handle program calls without any command."""
111 self._PrintHeadline()
112 self._PrintHelp()
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)
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()
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()
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
143 if returnCode != 0:
144 exit(returnCode)
146 print(f"Exporting code coverage information from UCDB file to Cobertura format ...")
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.")
152 coberturaPath = Path(args.cobertura)
154 print(f" IN -> UCIS (XML): {ucdbPath}")
155 print(f" OUT <- Cobertura (XML): {coberturaPath}")
157 parser = Parser(ucdbPath, args.mergeInstances)
158 model = parser.getCoberturaModel()
160 with coberturaPath.open('w') as file:
161 file.write(model.getXml().decode("utf-8"))
163 print()
165 try:
166 lineCoverage = model.linesCovered / model.linesValid * 100
167 except ZeroDivisionError:
168 lineCoverage = 100
170 try:
171 statementCoverage = parser.statementsCovered / parser.statementsCount * 100
172 except ZeroDivisionError:
173 statementCoverage = 100
175 print(dedent(f"""\
176 [DONE] Export and conversion complete.
177 Line coverage: {lineCoverage} %
178 Statement coverage: {statementCoverage} %
179 """)
180 )
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 )
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.")
204@export
205def main():
206 """
207 Entrypoint to start program execution.
209 This function should be called either from:
210 * ``if __name__ == "__main__":`` or
211 * ``console_scripts`` entry point configured via ``setuptools`` in ``setup.py``.
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)
229if __name__ == "__main__": 229 ↛ 230line 229 didn't jump to line 230 because the condition on line 229 was never true
230 main()