Test managementΒΆ

To use a test management adapter, it must be installed in the directory for User Python libraries. The adapter can then be configured and used in ecu.test. Concrete steps for setup and configuration can be found here.

The specific description of the test management adapter interface is explained in the API documentation.

Info

When implementing your own GUI in ecu.test, you must always use the ThreadDialog to prevent the application from freezing. Additional details are available in the TsShowDialogExample under Utility Examples.

An example implementation for a test management adapter could look like this:

TmUserAdapterExample.py:

"""
This module contains an example of a user test management adapter. The source code of this
example can be found in the ecu.test installation directory under
``Templates/DefaultData/UserPyModules/TestManagement``.

A user test management adapter must be placed in the UserPyModules directory of the current
workspace. If the implementation of the active adapter is changed, it is necessary to reload the
UserPyModules via select *Extras -> Update user libraries* in ecu.test.

Hint: In case of failures prefer to raise errors instead of printing error messages.

Hint: When implementing your own GUI, you must always use the ThreadDialog to prevent the
application from freezing. An example implementation for using the ThreadDialog can be found in the
``Templates/DefaultData/Utilities/ExampleUtilities/ShowDialogExample`` subdirectory of the ecu.test
installation directory.
"""

from __future__ import annotations

from tts.core.api.internalApi.Api import Api

from tts.interface.testManagement import AbstractTmUserAdapter, TmObjectInfo

from .TmsRestClientExample import TmsRestClientExample

# Required so that ecu.test can discover this module and recognize it as a user test management
# adapter.
MODULE_TYPE = AbstractTmUserAdapter.MODULE_TYPE


class TmUserAdapterExample(AbstractTmUserAdapter):

    __PROPERTY_AS_STR = 'Option 1'
    __PROPERTY_AS_BOOL = 'Option 2'
    __PROPERTY_AS_INT = 'Option 3'
    __PROPERTY_AS_FLOAT = 'Option 4'

    def __init__(self, properties: dict[str, int | str | float | bool | None]) -> None:
        """
        This method is called on adapter creation. It can be used to start any external dependencies
        needed by this adapter.

        The properties contain the configured properties for the concrete adapter as a dictionary.
            Key: The name of the property.
            Value: The value for the property.
        """
        self.__properties = properties
        self.__objectApi = Api().ObjectApi
        self.__tmsClient = TmsRestClientExample()

    def __del__(self) -> None:
        """
        This method is called when the instance is about to be destroyed. It can be used to dispose
        external dependencies.
        """
        if self.IsLoggedIn():
            self.Logout()

    @classmethod
    def GetName(cls) -> str:
        """
        The name of the user test management adapter.
        This name is displayed in the test management configuration.
        """
        return 'Test management user adapter example'

    @classmethod
    def GetAvailableProperties(cls) -> dict[str, dict]:
        """
        Defines the properties available for the user test management adapter. These properties can
        be configured in the test management configuration. The configured properties are passed to
        the `__init__` method when this adapter is created.
        Each property is identified by its name and described within its own dict containing
        information on 'Description', 'Type' and 'Default'. Available types are:
        'STRING', 'BOOL', 'INTEGER', 'FLOAT'.
        """
        props: dict[str, dict] = {
            TmUserAdapterExample.__PROPERTY_AS_STR: {
                'Description': 'This is Option 1 with a string.',
                'Type': 'STRING',
                'Default': 'default value',
            },
            TmUserAdapterExample.__PROPERTY_AS_BOOL: {
                'Description': 'This is Option 2 with a bool.',
                'Type': 'BOOL',
                'Default': False,
            },
            TmUserAdapterExample.__PROPERTY_AS_INT: {
                'Description': 'This is Option 3 with a int.',
                'Type': 'INTEGER',
                'Default': 42,
            },
            TmUserAdapterExample.__PROPERTY_AS_FLOAT: {
                'Description': 'This is Option 4 with a float.',
                'Type': 'FLOAT',
                'Default': 9.99999,
            },
        }
        return props

    def Login(self) -> bool:
        """
        Log in to the test management system.
        """
        self.__tmsClient.Login()
        return True

    def Logout(self) -> bool:
        """
        Log out of the test management system.
        """
        self.__tmsClient.Logout()
        return True

    def IsLoggedIn(self) -> bool:
        """
        Returns if there is currently an active user logged in. Depending on that information,
        actions will be activated in ecu.test.
        """
        return self.__tmsClient.IsLoggedIn()

    def ExportProject(self, filePaths: list[str]) -> None:
        """
        Exports PRJ files to the test management system.
        Convert here the ecu.test project to the specific tms object.
        """
        for filePath in filePaths:
            project = self.__objectApi.ProjectApi.OpenProject(filePath)
            tmsObject = self.__ConvertToTmsObject(project)
            newTmsId = self.__tmsClient.CreateItemAtTms(tmsObject)
            project.SetTestSuiteId(newTmsId)

    def ImportProject(self, targetDirectory: str, tmsIds: list[str]) -> None:
        """
        Imports projects from test management system and saves it as PRJ files.
        This method needs to contain:
        - the selection of the project within the test management system
        - the generation of the PRJ file using the ObjectApi.ProjectApi

        The passed tms Ids are defined using the GUI in ecu.test. You can choose between a selection
        and input mode. The selection mode uses FetchChildrenForProjectImport (if implemented) as
        source.
        """
        projectItemTypeId = 'TmsProjectId'
        for tmsId in tmsIds:
            tmsObject = self.__tmsClient.GetItemFromTms(tmsId)

            # use the item type id of the tmsObject to decide if it is a project or not
            if projectItemTypeId == tmsObject:
                project = self.__objectApi.ProjectApi.CreateProject()
                project.SetTestSuiteId(tmsId)
                self.__ConvertToEcuTest(project, tmsObject)
                project.Save(f'{targetDirectory}/myFancyProjectName_{tmsId}.prj')
            else:
                children = self.__tmsClient.GetChildrenForTmsId(tmsId)
                # use the tms id's of the children
                self.ImportProject(targetDirectory, children)

    def ExportPackage(self, filePaths: list[str]) -> None:
        """
        Exports PKG files to the test management system. Convert here the ecu.test packages to the
        specific object.
        """
        for filePath in filePaths:
            package = self.__objectApi.PackageApi.OpenPackage(filePath)
            tmsObject = self.__ConvertToTmsObject(package)
            newTmsId = self.__tmsClient.CreateItemAtTms(tmsObject)
            package.SetTestScriptId(newTmsId)

    def ImportPackage(self, targetDirectory: str, tmsIds: list[str]) -> None:
        """
        Imports packages from the test management system and saves it as PKG files.
        This method needs to contain:
        - the selection of the package within the test management system
        - the generation of the PKG file using the ObjectApi.PackageApi.

        The passed tms Ids are defined using the GUI in ecu.test. You can choose between a selection
        and input mode. The selection mode uses FetchChildrenForPackageImport (if implemented) as
        source.
        """
        packageItemTypeId = 'TmsPackageId'
        for tmsId in tmsIds:
            tmsObject = self.__tmsClient.GetItemFromTms(tmsId)

            # use the item type id of the tmsObject to decide if it is a package or not
            if packageItemTypeId == tmsObject:
                package = self.__objectApi.PackageApi.CreatePackage()
                package.SetTestScriptId(tmsId)
                self.__ConvertToEcuTest(package, tmsObject)
                package.Save(f'{targetDirectory}/myFancyPackageName_{tmsId}.pkg')
            else:
                children = self.__tmsClient.GetChildrenForTmsId(tmsId)
                # use the tms id's of the children
                self.ImportPackage(targetDirectory, children)

    def FetchChildrenForProjectImport(self, tmsId: str) -> list[TmObjectInfo]:
        """
        Returns the direct children (only first level) for the test management object with the
        specified tmsId. If the tmsId is empty the root objects are required. The result will be
        displayed in the dialog to import projects. For the chosen projects ImportProject() will be
        called.
        """
        fetchedChildren = []
        if tmsId:
            children = self.__tmsClient.GetChildrenForTmsId(tmsId)
            for child in children:
                fetchedChildren.append(
                    TmObjectInfo(
                        'TestProjectChild',
                        f'TestProjectID_{child}',
                        {
                            'author': 'dummy guy',
                            'date': '30.02.2025',
                            'description': f'Child object {child} from tms that represents a project at ecu.test.',
                        },
                    ),
                )
        else:
            fetchedChildren.append(
                TmObjectInfo(
                    'TestProject',
                    'TestProjectID_0',
                    {
                        'author': 'dummy guy',
                        'date': '30.02.2025',
                        'description': 'Object from tms that represents a project at ecu.test.',
                    },
                ),
            )

        return fetchedChildren

    def FetchChildrenForPackageImport(self, tmsId: str) -> list[TmObjectInfo]:
        """
        Returns the direct children (only first level) for the test management object with the
        specified tmsId. If the tmsId is empty the root objects are required. The result will be
        displayed in the dialog to import packages. For the chosen packages ImportPackage() will be
        called.
        """
        fetchedChildren = []
        if tmsId:
            children = self.__tmsClient.GetChildrenForTmsId(tmsId)
            for child in children:
                fetchedChildren.append(
                    TmObjectInfo(
                        'TestPackageChild',
                        f'TestPackageID_{child}',
                        {
                            'author': 'dummy guy',
                            'date': '30.02.2025',
                            'description': f'Child object {child} from tms that represents a package at ecu.test.',
                        },
                    )
                )
        else:
            fetchedChildren.append(
                TmObjectInfo(
                    'TestPackage',
                    'TestPackageID_0',
                    {
                        'author': 'dummy guy',
                        'date': '30.02.2025',
                        'description': 'Object from tms that represents a package at ecu.test.',
                    },
                )
            )

        return fetchedChildren

    def __ConvertToTmsObject(self, ecuTestObject: str) -> str:
        print(f'Convert ecu.test object {ecuTestObject} to a tms object.')
        return 'DummyTmsObject'

    def __ConvertToEcuTest(self, targetEcuTestObject, tmsObject) -> None:
        print(f'Convert tms object {tmsObject} to ecu.test object {targetEcuTestObject}')

TmsRestClientExample.py:

"""
This module contains an example of a user test management adapter. The source code of this
example can be found in the ecu.test installation directory under
``Templates/DefaultData/UserPyModules/TestManagement``.

A user test management adapter must be placed in the UserPyModules directory of the current
workspace. If the implementation of the active adapter is changed, it is necessary to reload the
UserPyModules via select *Extras -> Update user libraries* in ecu.test.

Hint: In case of failures prefer to raise errors instead of printing error messages.
"""

from __future__ import annotations


class TmsRestClientExample:
    """
    Class that knows the concrete communication api (e.g. OpenAPI-client via Rest) to the test
    management system.
    """

    def __init__(self) -> None:
        print('TMS: Setup concrete communication details to test management system.')
        self.__isLoggedIn = False

    def Login(self) -> None:
        print('TMS: Process login.')
        self.__isLoggedIn = True

    def Logout(self) -> None:
        print('TMS: Process logout.')
        self.__isLoggedIn = False

    def IsLoggedIn(self) -> bool:
        return self.__isLoggedIn

    def CreateItemAtTms(self, tmsObject: str) -> str:
        print(f'TMS: Create new item at tms for {tmsObject}.')
        return 'newTmsId'

    def GetItemFromTms(self, tmsId: str) -> str:
        print(f'TMS: Get item from tms for id {tmsId}.')
        return 'DummyTmsItem'

    def GetChildrenForTmsId(self, tmsId: str) -> list[str]:
        print(f'TMS: Get children from tms for id {tmsId}.')
        return ['Child1', 'Child2']