Source code for pyEDAA.Reports.CodeCoverage.UCIS.Bins

# ==================================================================================================================== #
#              _____ ____    _        _      ____                       _                                              #
#  _ __  _   _| ____|  _ \  / \      / \    |  _ \ ___ _ __   ___  _ __| |_ ___                                        #
# | '_ \| | | |  _| | | | |/ _ \    / _ \   | |_) / _ \ '_ \ / _ \| '__| __/ __|                                       #
# | |_) | |_| | |___| |_| / ___ \  / ___ \ _|  _ <  __/ |_) | (_) | |  | |_\__ \                                       #
# | .__/ \__, |_____|____/_/   \_\/_/   \_(_)_| \_\___| .__/ \___/|_|   \__|___/                                       #
# |_|    |___/                                        |_|                                                              #
# ==================================================================================================================== #
# Authors:                                                                                                             #
#   Patrick Lehmann                                                                                                    #
#                                                                                                                      #
# License:                                                                                                             #
# ==================================================================================================================== #
# Copyright 2026-2026 Electronic Design Automation Abstraction (EDA²)                                                  #
#                                                                                                                      #
# Licensed under the Apache License, Version 2.0 (the "License");                                                      #
# you may not use this file except in compliance with the License.                                                     #
# You may obtain a copy of the License at                                                                              #
#                                                                                                                      #
#   http://www.apache.org/licenses/LICENSE-2.0                                                                         #
#                                                                                                                      #
# Unless required by applicable law or agreed to in writing, software                                                  #
# distributed under the License is distributed on an "AS IS" BASIS,                                                    #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.                                             #
# See the License for the specific language governing permissions and                                                  #
# limitations under the License.                                                                                       #
#                                                                                                                      #
# SPDX-License-Identifier: Apache-2.0                                                                                  #
# ==================================================================================================================== #
#
"""
The bins of the UCIS XML interchange format: a coverage item's count - how often it was covered -, the tests, which
covered it, and its goal.
"""
from __future__                                import annotations

from typing                                    import Iterable, Optional as Nullable, Self

from lxml.etree                                import _Element
from pyTooling.Common                          import getFullyQualifiedName
from pyTooling.Decorators                      import export, readonly
from pyTooling.MetaClasses                     import ExtendedType

from pyEDAA.Reports.CodeCoverage.UCIS.Elements import Base, ObjectAttributesMixin, UserAttribute


[docs] @export class BinContents(metaclass=ExtendedType, slots=True): """ A ``<contents>`` of a bin: how often its coverage item was covered, the tests, which covered it, and the components of its unique ID. It is a value, as a path is: it names no parent. """ _coverageCount: int #: How often the coverage item was covered. _historyNodeIDs: list[int] #: The IDs of the history nodes, which covered the coverage item. _nameComponent: Nullable[str] #: The name component of the coverage item's unique ID. _typeComponent: Nullable[str] #: The type component of the coverage item's unique ID.
[docs] def __init__( self, coverageCount: int, historyNodeIDs: Nullable[Iterable[int]] = None, nameComponent: Nullable[str] = None, typeComponent: Nullable[str] = None ) -> None: """ Initialize the contents of a bin. :param coverageCount: How often the coverage item was covered. :param historyNodeIDs: Optional, the IDs of the history nodes, which covered the coverage item. Default: ``None``. :param nameComponent: Optional, the name component of the coverage item's unique ID. Default: ``None``. :param typeComponent: Optional, the type component of the coverage item's unique ID, e.g. ``:5:`` for a statement. Default: ``None``. :raises ValueError: If parameter ``coverageCount`` is ``None``. :raises TypeError: If parameter ``coverageCount`` isn't of type :class:`int`. :raises ValueError: If parameter ``coverageCount`` is negative. :raises TypeError: If parameter ``historyNodeIDs`` isn't iterable. :raises TypeError: If parameter ``historyNodeIDs`` contains an element not of type :class:`int`. :raises TypeError: If parameter ``nameComponent`` isn't of type :class:`str`. :raises TypeError: If parameter ``typeComponent`` isn't of type :class:`str`. """ if coverageCount is None: raise ValueError("Parameter 'coverageCount' is None.") elif not isinstance(coverageCount, int) or isinstance(coverageCount, bool): ex = TypeError("Parameter 'coverageCount' is not of type 'int'.") ex.add_note(f"Got type '{getFullyQualifiedName(coverageCount)}'.") raise ex elif coverageCount < 0: ex = ValueError("Parameter 'coverageCount' is negative.") ex.add_note(f"Got value '{coverageCount}'.") raise ex if nameComponent is not None and not isinstance(nameComponent, str): ex = TypeError("Parameter 'nameComponent' is not of type 'str'.") ex.add_note(f"Got type '{getFullyQualifiedName(nameComponent)}'.") raise ex if typeComponent is not None and not isinstance(typeComponent, str): ex = TypeError("Parameter 'typeComponent' is not of type 'str'.") ex.add_note(f"Got type '{getFullyQualifiedName(typeComponent)}'.") raise ex self._coverageCount = coverageCount self._historyNodeIDs = historyNodeIDList = [] self._nameComponent = nameComponent self._typeComponent = typeComponent if historyNodeIDs is not None: if not isinstance(historyNodeIDs, Iterable): ex = TypeError("Parameter 'historyNodeIDs' is not iterable.") ex.add_note(f"Got type '{getFullyQualifiedName(historyNodeIDs)}'.") raise ex for historyNodeID in historyNodeIDs: if not isinstance(historyNodeID, int) or isinstance(historyNodeID, bool): ex = TypeError("Parameter 'historyNodeIDs' contains an element not of type 'int'.") ex.add_note(f"Got type '{getFullyQualifiedName(historyNodeID)}'.") raise ex historyNodeIDList.append(historyNodeID)
[docs] @classmethod def Parse(cls, element: _Element) -> Self: """ Parse the contents of a bin from its ``<contents>`` element. :param element: The ``<contents>`` element. :returns: The contents. """ return cls( int(element.attrib["coverageCount"]), [int(idElement.text) for idElement in element.iterfind("{*}historyNodeId")], element.attrib.get("nameComponent"), element.attrib.get("typeComponent") )
@readonly def CoverageCount(self) -> int: """ Read-only property to access how often the coverage item was covered (:attr:`_coverageCount`). :returns: The count, e.g. how often a statement ran. """ return self._coverageCount @readonly def HistoryNodeIDs(self) -> list[int]: """ Read-only property to access the IDs of the history nodes, which covered the item (:attr:`_historyNodeIDs`). :returns: The IDs, in the order the report lists them. """ return self._historyNodeIDs @readonly def NameComponent(self) -> Nullable[str]: """ Read-only property to access the name component of the coverage item's unique ID (:attr:`_nameComponent`). :returns: The name component; ``None``, if the report states none. """ return self._nameComponent @readonly def TypeComponent(self) -> Nullable[str]: """ Read-only property to access the type component of the coverage item's unique ID (:attr:`_typeComponent`). :returns: The type component, e.g. ``:5:``; ``None``, if the report states none. """ return self._typeComponent
[docs] @export class Bin(Base, ObjectAttributesMixin): """ A bin of a coverage item: its contents, its goal and its attributes. A statement has ``<bin>`` elements, a branch ``<branchBin>`` elements. The element owning the bin takes it as a parameter and becomes its parent. """ _parent: Nullable[Base] #: The element owning the bin. _contents: BinContents #: How often the coverage item was covered, and by which tests. _coverageCountGoal: Nullable[int] #: How often the coverage item must be covered.
[docs] def __init__( self, contents: BinContents, coverageCountGoal: Nullable[int] = None, alias: Nullable[str] = None, excluded: bool = False, excludedReason: Nullable[str] = None, weight: int = 1, userAttributes: Nullable[Iterable[UserAttribute]] = None ) -> None: """ Initialize the bin. :param contents: How often the coverage item was covered, and by which tests. :param coverageCountGoal: Optional, how often the coverage item must be covered. Default: ``None``. :param alias: Optional, an alias of the bin's name. Default: ``None``. :param excluded: Optional, whether the bin is excluded from the coverage. Default: ``False``. :param excludedReason: Optional, why the bin is excluded. Default: ``None``. :param weight: Optional, weight of the bin in computing the coverage. Default: ``1``. :param userAttributes: Optional, the user-defined attributes. Default: ``None``. :raises ValueError: If parameter ``contents`` is ``None``. :raises TypeError: If parameter ``contents`` isn't of type :class:`BinContents`. :raises TypeError: If parameter ``coverageCountGoal`` isn't of type :class:`int`. :raises ValueError: If parameter ``coverageCountGoal`` is negative. :raises TypeError: If parameter ``alias`` isn't of type :class:`str`. :raises ValueError: If parameter ``excluded`` is ``None``. :raises TypeError: If parameter ``excluded`` isn't of type :class:`bool`. :raises TypeError: If parameter ``excludedReason`` isn't of type :class:`str`. :raises ValueError: If parameter ``weight`` is ``None``. :raises TypeError: If parameter ``weight`` isn't of type :class:`int`. :raises ValueError: If parameter ``weight`` is negative. :raises TypeError: If parameter ``userAttributes`` isn't iterable. :raises TypeError: If parameter ``userAttributes`` contains an element not of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Elements.UserAttribute`. """ super().__init__(userAttributes) ObjectAttributesMixin.__init__(self, alias, excluded, excludedReason, weight) if contents is None: raise ValueError("Parameter 'contents' is None.") elif not isinstance(contents, BinContents): ex = TypeError("Parameter 'contents' is not of type 'BinContents'.") ex.add_note(f"Got type '{getFullyQualifiedName(contents)}'.") raise ex if coverageCountGoal is not None: if not isinstance(coverageCountGoal, int) or isinstance(coverageCountGoal, bool): ex = TypeError("Parameter 'coverageCountGoal' is not of type 'int'.") ex.add_note(f"Got type '{getFullyQualifiedName(coverageCountGoal)}'.") raise ex elif coverageCountGoal < 0: ex = ValueError("Parameter 'coverageCountGoal' is negative.") ex.add_note(f"Got value '{coverageCountGoal}'.") raise ex self._parent = None self._contents = contents self._coverageCountGoal = coverageCountGoal
[docs] @classmethod def Parse(cls, element: _Element) -> Self: """ Parse a bin, its contents and its user-defined attributes from its element. The element is e.g. a ``<bin>`` or a ``<branchBin>``. :param element: The bin's element. :returns: The bin. :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states. """ goal = element.attrib.get("coverageCountGoal") return cls( BinContents.Parse(element.find("{*}contents")), None if goal is None else int(goal), element.attrib.get("alias"), cls._ParseBoolean(element.attrib.get("excluded", "false")), element.attrib.get("excludedReason"), int(element.attrib.get("weight", "1")), cls._ParseUserAttributes(element) )
@readonly def Parent(self) -> Nullable[Base]: """ Read-only property to access the element owning the bin (:attr:`_parent`). :returns: The element, e.g. a :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Blocks.Statement`; ``None``, if the bin belongs to none. """ return self._parent @readonly def Contents(self) -> BinContents: """ Read-only property to access the contents of the bin (:attr:`_contents`). :returns: How often the coverage item was covered, and by which tests. """ return self._contents @readonly def CoverageCountGoal(self) -> Nullable[int]: """ Read-only property to access how often the coverage item must be covered (:attr:`_coverageCountGoal`). :returns: The goal; ``None``, if the report states none. """ return self._coverageCountGoal