Source code for pyEDAA.Reports.Unittesting.OpenTestReporting.Records

# ==================================================================================================================== #
#              _____ ____    _        _      ____                       _                                              #
#  _ __  _   _| ____|  _ \  / \      / \    |  _ \ ___ _ __   ___  _ __| |_ ___                                        #
# | '_ \| | | |  _| | | | |/ _ \    / _ \   | |_) / _ \ '_ \ / _ \| '__| __/ __|                                       #
# | |_) | |_| | |___| |_| / ___ \  / ___ \ _|  _ <  __/ |_) | (_) | |  | |_\__ \                                       #
# | .__/ \__, |_____|____/_/   \_\/_/   \_(_)_| \_\___| .__/ \___/|_|   \__|___/                                       #
# |_|    |___/                                        |_|                                                              #
# ==================================================================================================================== #
# 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 records of Open Test Reporting's XML formats: the infrastructure of an execution and the result of a test or
container.

The infrastructure belongs to an :class:`~pyEDAA.Reports.Unittesting.OpenTestReporting.Execution`, a result to a
:class:`~pyEDAA.Reports.Unittesting.OpenTestReporting.Node` of the tree in
:mod:`pyEDAA.Reports.Unittesting.OpenTestReporting`.
"""
from __future__                                     import annotations

from enum                                           import Enum
from typing                                         import TYPE_CHECKING, Dict, Optional as Nullable

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

if TYPE_CHECKING:
	from pyEDAA.Reports.Unittesting.OpenTestReporting import Execution, Node


[docs] @export class ResultStatus(Enum): """An enumeration of the statuses of a test's or container's result (``<result status>``).""" Successful = "SUCCESSFUL" #: Executed and passed. Skipped = "SKIPPED" #: Not executed, e.g. a disabled test. Aborted = "ABORTED" #: Started, but aborted, e.g. by a failed assumption. Failed = "FAILED" #: Executed and failed, e.g. by a failed assertion or an exception. Errored = "ERRORED" #: Executed, but an error outside the test occurred.
[docs] @export class Infrastructure(metaclass=ExtendedType, slots=True): """ The infrastructure the tests were run on (``<infrastructure>``). The core namespace states the host, the user, the operating system and the CPU cores. Elements of extensions, like the Java version, are collected in :data:`Extensions`. """ _parent: Nullable[Execution] #: The execution the infrastructure belongs to. _hostName: Nullable[str] #: Name of the host. _userName: Nullable[str] #: Name of the user. _operatingSystem: Nullable[str] #: Name of the operating system. _cpuCoreCount: Nullable[int] #: Count of CPU cores. _extensions: Dict[str, str] #: Texts and attribute values of extension elements.
[docs] def __init__( self, hostName: Nullable[str] = None, userName: Nullable[str] = None, operatingSystem: Nullable[str] = None, cpuCoreCount: Nullable[int] = None, *, parent: Nullable[Execution] = None ) -> None: """ Initializes an infrastructure description, and sets it as the infrastructure of its execution. :param hostName: Optional, name of the host. :param userName: Optional, name of the user. :param operatingSystem: Optional, name of the operating system. :param cpuCoreCount: Optional, count of CPU cores. :param parent: Optional, the execution the infrastructure belongs to; it becomes the execution's :attr:`~pyEDAA.Reports.Unittesting.OpenTestReporting.Execution.Infrastructure`. Default: ``None``. :raises TypeError: If parameter 'hostName' is not of type :class:`str`. :raises TypeError: If parameter 'userName' is not of type :class:`str`. :raises TypeError: If parameter 'operatingSystem' is not of type :class:`str`. :raises TypeError: If parameter 'cpuCoreCount' is not of type :class:`int`. :raises TypeError: If parameter 'parent' is not of type :class:`~pyEDAA.Reports.Unittesting.OpenTestReporting.Execution`. """ from pyEDAA.Reports.Unittesting.OpenTestReporting import Execution for parameterName, value in ( ("hostName", hostName), ("userName", userName), ("operatingSystem", operatingSystem) ): if value is not None and not isinstance(value, str): ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.") ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") raise ex if cpuCoreCount is not None and not isinstance(cpuCoreCount, int): ex = TypeError("Parameter 'cpuCoreCount' is not of type 'int'.") ex.add_note(f"Got type '{getFullyQualifiedName(cpuCoreCount)}'.") raise ex if parent is not None and not isinstance(parent, Execution): ex = TypeError("Parameter 'parent' is not of type 'Execution'.") ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.") raise ex self._parent = parent self._hostName = hostName self._userName = userName self._operatingSystem = operatingSystem self._cpuCoreCount = cpuCoreCount self._extensions = {} if parent is not None: parent._infrastructure = self
@readonly def Parent(self) -> Nullable[Execution]: """ Read-only property to access the execution the infrastructure belongs to (:attr:`_parent`). :returns: The execution; ``None`` if the infrastructure belongs to no execution. """ return self._parent @readonly def HostName(self) -> Nullable[str]: """ Read-only property to access the name of the host (:attr:`_hostName`). :returns: The host name, or ``None`` if it wasn't reported. """ return self._hostName @readonly def UserName(self) -> Nullable[str]: """ Read-only property to access the name of the user (:attr:`_userName`). :returns: The user name, or ``None`` if it wasn't reported. """ return self._userName @readonly def OperatingSystem(self) -> Nullable[str]: """ Read-only property to access the name of the operating system (:attr:`_operatingSystem`). :returns: The operating system, or ``None`` if it wasn't reported. """ return self._operatingSystem @readonly def CPUCoreCount(self) -> Nullable[int]: """ Read-only property to access the count of CPU cores (:attr:`_cpuCoreCount`). :returns: The count of CPU cores, or ``None`` if it wasn't reported. """ return self._cpuCoreCount @readonly def Extensions(self) -> Dict[str, str]: """ Read-only property to access the texts and attribute values of extension elements (:attr:`_extensions`). An element's text is stored by the element's local name, e.g. ``javaVersion``, an attribute's value by the local name and the attribute's name, e.g. ``heapSize.max``. :returns: The dictionary of extension values. """ return self._extensions
[docs] @export class Result(metaclass=ExtendedType, slots=True): """ The result of a test or container (``<result>``). Besides a status, a result can state the reason for it, e.g. why a test was skipped, and the exception ending a test (Java extension, ``<java:throwable>``). """ _parent: Nullable[Node] #: The test or container the result belongs to. _status: Nullable[ResultStatus] #: Status of the result. _reason: Nullable[str] #: Reason for the status. _exceptionType: Nullable[str] #: Fully qualified type of the exception. _isAssertionError: Nullable[bool] #: True, if the exception is an assertion error. _stackTrace: Nullable[str] #: Stack trace of the exception, with its type and message.
[docs] def __init__( self, status: Nullable[ResultStatus] = None, reason: Nullable[str] = None, exceptionType: Nullable[str] = None, isAssertionError: Nullable[bool] = None, stackTrace: Nullable[str] = None, *, parent: Nullable[Node] = None ) -> None: """ Initializes a result, and sets it as the result of its test or container. :param status: Optional, status of the result. :param reason: Optional, reason for the status. :param exceptionType: Optional, fully qualified type of the exception. :param isAssertionError: Optional, true, if the exception is an assertion error. :param stackTrace: Optional, stack trace of the exception. :param parent: Optional, the test or container the result belongs to; it becomes the node's :attr:`~pyEDAA.Reports.Unittesting.OpenTestReporting.Node.Result`. Default: ``None``. :raises TypeError: If parameter 'status' is not of type :class:`ResultStatus`. :raises TypeError: If parameter 'reason' is not of type :class:`str`. :raises TypeError: If parameter 'exceptionType' is not of type :class:`str`. :raises TypeError: If parameter 'isAssertionError' is not of type :class:`bool`. :raises TypeError: If parameter 'stackTrace' is not of type :class:`str`. :raises TypeError: If parameter 'parent' is not of type :class:`~pyEDAA.Reports.Unittesting.OpenTestReporting.Node`. """ from pyEDAA.Reports.Unittesting.OpenTestReporting import Node if status is not None and not isinstance(status, ResultStatus): ex = TypeError("Parameter 'status' is not of type 'ResultStatus'.") ex.add_note(f"Got type '{getFullyQualifiedName(status)}'.") raise ex for parameterName, value in ( ("reason", reason), ("exceptionType", exceptionType), ("stackTrace", stackTrace) ): if value is not None and not isinstance(value, str): ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.") ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") raise ex if isAssertionError is not None and not isinstance(isAssertionError, bool): ex = TypeError("Parameter 'isAssertionError' is not of type 'bool'.") ex.add_note(f"Got type '{getFullyQualifiedName(isAssertionError)}'.") raise ex if parent is not None and not isinstance(parent, Node): ex = TypeError("Parameter 'parent' is not of type 'Node'.") ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.") raise ex self._parent = parent self._status = status self._reason = reason self._exceptionType = exceptionType self._isAssertionError = isAssertionError self._stackTrace = stackTrace if parent is not None: parent._result = self
@readonly def Parent(self) -> Nullable[Node]: """ Read-only property to access the test or container the result belongs to (:attr:`_parent`). :returns: The node; ``None`` if the result belongs to no node. """ return self._parent @readonly def Status(self) -> Nullable[ResultStatus]: """ Read-only property to access the status of the result (:attr:`_status`). :returns: The status, or ``None`` if it wasn't reported. """ return self._status @readonly def Reason(self) -> Nullable[str]: """ Read-only property to access the reason for the status (:attr:`_reason`). :returns: The reason, or ``None`` if it wasn't reported. """ return self._reason @readonly def ExceptionType(self) -> Nullable[str]: """ Read-only property to access the fully qualified type of the exception (:attr:`_exceptionType`). A failed JUnit assertion raises ``org.opentest4j.AssertionFailedError``. :returns: The exception's type, or ``None`` if no exception was reported. """ return self._exceptionType @readonly def IsAssertionError(self) -> Nullable[bool]: """ Read-only property to access whether the exception is an assertion error (:attr:`_isAssertionError`). :returns: True, if the exception is an assertion error, or ``None`` if no exception was reported. """ return self._isAssertionError @readonly def StackTrace(self) -> Nullable[str]: """ Read-only property to access the stack trace of the exception (:attr:`_stackTrace`). :returns: The stack trace, starting with the exception's type and message, or ``None`` if no exception was reported. """ return self._stackTrace @readonly def Message(self) -> Nullable[str]: """ Read-only property to return the message explaining the status. It's the reason, if one was reported, else the exception's message: the stack trace's text before its first frame, without the exception's type. :returns: The message, or ``None`` if neither a reason nor an exception with a message was reported. """ if self._reason is not None: return self._reason elif self._stackTrace is None: return None message = self._stackTrace.split("\n\tat ", 1)[0].rstrip("\r\n") if message == self._exceptionType: return None return message.removeprefix(f"{self._exceptionType}: ")