Skip to content

Commit

Permalink
Merge branch 'master' into itf-ykush-auxiliary
Browse files Browse the repository at this point in the history
  • Loading branch information
yannpoupon authored Jan 23, 2023
2 parents 147f872 + ae4202a commit 869199f
Show file tree
Hide file tree
Showing 61 changed files with 921 additions and 594 deletions.
6 changes: 6 additions & 0 deletions docs/advanced_usage/advanced_config_file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ that will have exclusive access to the communication channel. A
the created :py:class:`~pykiso.lib.auxiliaries.proxy_auxiliary.ProxyAuxiliary`. This allows to
keep a minimalistic :py:class:`~pykiso.connector.CChannel` implementation.

Access to attributes and methods of the defined communication channel that has been
attached to the created :py:class:`~pykiso.lib.auxiliaries.proxy_auxiliary.ProxyAuxiliary`
is still possible, but keep in mind that if one auxiliary modifies one the communication
channel's attributes, every other auxiliary sharing this communication channel will be
affected by this change.

An illustration of the resulting internal setup can be found at :ref:`proxy_aux`.

In other words, if you define the following YAML configuration file:
Expand Down
33 changes: 8 additions & 25 deletions docs/advanced_usage/how_to_connector.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,9 @@ This interface enforces the implementation of the following methods:
- :py:meth:`~pykiso.connector.CChannel._cc_close`: close the communication.
Does not take any argument.
- :py:meth:`~pykiso.connector.CChannel._cc_send`: send data if the communication is open.
Requires one positional argument ``msg`` and one keyword argument ``raw``, used to serialize the data
before sending it.
Requires one positional argument ``msg`` .
- :py:meth:`~pykiso.connector.CChannel._cc_receive`: receive data if the communication is open.
Requires one positional argument ``timeout`` and one keyword argument ``raw``, used to deserialize
the data when receiving it.
Requires one positional argument ``timeout`` .


Class definition and instanciation
Expand Down Expand Up @@ -113,19 +111,13 @@ The connector then becomes:
def _cc_close(self):
self.my_connection.close()
def _cc_send(self, data: Union[Data, bytes], raw = False):
if raw:
data_bytes = data
else:
data_bytes = data.serialize()
def _cc_send(self, data: bytes):
self.my_connection.send(data_bytes)
def _cc_receive(self, timeout, raw = False):
def _cc_receive(self, timeout) -> Optional[bytes]:
received_data = self.my_connection.receive(timeout=timeout)
if received_data:
if not raw:
data = Data.deserialize(received_data)
return data
return received_data
.. note::
The API used in this example for the fictive *my_connection* module
Expand All @@ -146,15 +138,11 @@ see the example below with the cc_pcan_can connector and the return of the remot
.. code:: python
def _cc_receive(
self, timeout: float = 0.0001, raw: bool = False
self, timeout: float = 0.0001
) -> Dict[str, Union[MessageType, int]]:
"""Receive a can message using configured filters.
If raw parameter is set to True return received message as it is (bytes)
otherwise test entity protocol format is used and Message class type is returned.
:param timeout: timeout applied on reception
:param raw: boolean use to select test entity protocol format
:return: the received data and the source can id
"""
Expand All @@ -165,8 +153,6 @@ see the example below with the cc_pcan_can connector and the return of the remot
frame_id = received_msg.arbitration_id
payload = received_msg.data
timestamp = received_msg.timestamp
if not raw:
payload = Message.parse_packet(payload)
log.internal_debug(f"received CAN Message: {frame_id}, {payload}, {timestamp}")
return {"msg": payload, "remote_id": frame_id}
else:
Expand All @@ -186,15 +172,12 @@ see example below with the cc_pcan_can connector and the additional remote_id pa

.. code:: python
def _cc_send(self, msg: MessageType, raw: bool = False, **kwargs) -> None:
def _cc_send(self, msg: MessageType, **kwargs) -> None:
"""Send a CAN message at the configured id.
If remote_id parameter is not given take configured ones, in addition if
raw is set to True take the msg parameter as it is otherwise parse it using
test entity protocol format.
If remote_id parameter is not given take configured ones
:param msg: data to send
:param raw: boolean use to select test entity protocol format
:param kwargs: named arguments
"""
Expand Down
10 changes: 10 additions & 0 deletions docs/whats_new/version_ongoing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,13 @@ Ykush Auxiliary
Auxiliary that can be used to power on and off the ports of an Ykush USB Hub.

See :ref:`ykush_auxiliary`


Internal creation of proxy auxiliaries
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

It is no longer necessary to manually defined a ``ProxyAuxiliary`` with
``CCProxy``s yourself. If you simply pass the communication channel to
each auxiliary that has to share it, ``pykiso`` will do the rest for you.


58 changes: 58 additions & 0 deletions examples/test_stepreport/stepreport_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,61 @@ def test_run(self):
self.assertAlmostEqual(voltage, 4, delta=1, msg="Check voltage device")

logging.info(f"I HAVE RUN 0.1.1 for tag {self.tag}!")


@pykiso.define_test_parameters(
suite_id=1,
case_id=3,
)
class WrapLongTextTest(pykiso.BasicTest):
"""This test shows wrapping of long results into foldable html elements"""

def setUp(self):
"""Set header information and check setup conditions"""
super().setUp()
# additional data to include in the step-report
self.step_report.header["Version_device"] = "2022-1234"

def test_run(self):
"""Write long results to the step report to show how foldable html
elements get used to make the report overview more readable
"""
logging.info(
f"--------------- RUN: {self.test_suite_id}, {self.test_case_id} ---------------"
)

# data to test
device_on = True
actual_dummy_result = {"result": True}
expected_dummy_result = {
"result": "pykiso is an integration test framework. With it, it is possible to write: Whitebox integration tests directly on my target device, Graybox integration tests to make sure the communication-link with my target device is working as expected, Blackbox integration tests to make sure my external device interfaces are working as expected",
}

# check that it works with multiple tables
self.step_report.current_table = "First table"

self.assertEqual(
expected_dummy_result,
expected_dummy_result,
msg="The very long message should be wrapped",
)

self.step_report.current_table = "Second table"

with self.subTest("Non critical checks"):
# This check will fail but the test continues
self.assertFalse(device_on, msg="Some check")

# assert with custom message
# assert msg overwritten when step_report_message not null
self.step_report.message = "Big data message"

self.assertEqual(
expected_dummy_result, expected_dummy_result, msg="Check big data"
)

self.assertEqual(
expected_dummy_result, actual_dummy_result, msg="Check big data"
)

logging.info(f"I HAVE RUN 0.1.1 for tag {self.tag}!")
17 changes: 4 additions & 13 deletions src/pykiso/auxiliary.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
import time
from typing import Any

from .logging_initializer import add_logging_level
from .test_setup.config_registry import ConfigRegistry
from .types import MsgType
from pykiso.logging_initializer import add_internal_log_levels
from pykiso.test_setup.config_registry import ConfigRegistry
from pykiso.types import MsgType

log = logging.getLogger(__name__)

Expand All @@ -38,18 +38,9 @@ class AuxiliaryCommon(metaclass=abc.ABCMeta):
multiprocessing and thread auxiliary interface.
"""

def __new__(cls: AuxiliaryCommon, *args, **kwargs) -> AuxiliaryCommon:
"""Create instance and add internal kiso log levels in
case the auxiliary is used outside the pykiso context
"""
if not hasattr(logging, "INTERNAL_WARNING"):
add_logging_level("INTERNAL_WARNING", logging.WARNING + 1)
add_logging_level("INTERNAL_INFO", logging.INFO + 1)
add_logging_level("INTERNAL_DEBUG", logging.DEBUG + 1)
return super(AuxiliaryCommon, cls).__new__(cls)

def __init__(self) -> None:
"""Auxiliary common attributes initialization."""
add_internal_log_levels()
self.name = None
self.queue_in = None
self.lock = None
Expand Down
37 changes: 23 additions & 14 deletions src/pykiso/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@
"""
import abc
import logging
import multiprocessing
import pathlib
import threading
from typing import Dict, Optional

from .types import MsgType, PathType

log = logging.getLogger(__name__)


class Connector(abc.ABC):
"""Abstract interface for all connectors to inherit from.
Expand Down Expand Up @@ -96,28 +100,35 @@ def close(self) -> None:
with self._lock:
self._cc_close()

def cc_send(self, msg: MsgType, raw: bool = False, **kwargs) -> None:
def cc_send(self, msg: MsgType, *args, **kwargs) -> None:
"""Send a thread-safe message on the channel and wait for an acknowledgement.
:param msg: message to send
:param raw: should the message be converted as pykiso.Message
or sent as it is
:param kwargs: named arguments
"""
if ("raw" in kwargs) or args:
log.internal_warning(
"Use of 'raw' keyword argument is deprecated. It won't be passed to '_cc_send'."
)
with self._lock_tx:
self._cc_send(msg=msg, raw=raw, **kwargs)
self._cc_send(msg=msg, **kwargs)

def cc_receive(self, timeout: float = 0.1, raw: bool = False) -> dict:
def cc_receive(
self, timeout: float = 0.1, *args, **kwargs
) -> Dict[str, Optional[bytes]]:
"""Read a thread-safe message on the channel and send an acknowledgement.
:param timeout: time in second to wait for reading a message
:param raw: should the message be returned as pykiso.Message or
sent as it is
:param kwargs: named arguments
:return: the received message
"""
if ("raw" in kwargs) or args:
log.internal_warning(
"Use of 'raw' keyword argument is deprecated. It won't be passed to '_cc_receive'."
)
with self._lock_rx:
return self._cc_receive(timeout=timeout, raw=raw)
return self._cc_receive(timeout=timeout, **kwargs)

@abc.abstractmethod
def _cc_open(self) -> None:
Expand All @@ -130,23 +141,21 @@ def _cc_close(self) -> None:
pass

@abc.abstractmethod
def _cc_send(self, msg: MsgType, raw: bool = False, **kwargs) -> None:
def _cc_send(self, msg: MsgType, **kwargs) -> None:
"""Sends the message on the channel.
:param msg: Message to send out
:param raw: send raw message without further work (default: False)
:param kwargs: named arguments
"""
pass

@abc.abstractmethod
def _cc_receive(self, timeout: float, raw: bool = False) -> dict:
def _cc_receive(self, timeout: float, **kwargs) -> Dict[str, Optional[bytes]]:
"""How to receive something from the channel.
:param timeout: Time to wait in second for a message to be received
:param raw: send raw message without further work (default: False)
:return: message.Message() - If one received / None - If not
:param kwargs: named arguments
:return: dictionary containing the received bytes if successful, otherwise None
"""
pass

Expand Down
17 changes: 4 additions & 13 deletions src/pykiso/interfaces/dt_auxiliary.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from typing import Any, Callable, List, Optional

from ..exceptions import AuxiliaryCreationError
from ..logging_initializer import add_logging_level, initialize_loggers
from ..logging_initializer import add_internal_log_levels, initialize_loggers

log = logging.getLogger(__name__)

Expand All @@ -49,16 +49,6 @@ class DTAuxiliaryInterface(abc.ABC):
for the reception and one for the transmmission.
"""

def __new__(cls, *args, **kwargs):
"""Create instance and add internal kiso log levels in
case the auxiliary is used outside the pykiso context
"""
if not hasattr(logging, "INTERNAL_WARNING"):
add_logging_level("INTERNAL_WARNING", logging.WARNING + 1)
add_logging_level("INTERNAL_INFO", logging.INFO + 1)
add_logging_level("INTERNAL_DEBUG", logging.DEBUG + 1)
return super(DTAuxiliaryInterface, cls).__new__(cls)

def __init__(
self,
name: str = None,
Expand All @@ -82,10 +72,11 @@ def __init__(
:param auto_start: determine if the auxiliayry is automatically
started (magic import) or manually (by user)
"""
initialize_loggers(activate_log)
add_internal_log_levels()
self.name = name
self.is_proxy_capable = is_proxy_capable
self.auto_start = auto_start
initialize_loggers(activate_log)
self.lock = threading.RLock()
self.stop_tx = threading.Event()
self.stop_rx = threading.Event()
Expand Down Expand Up @@ -209,7 +200,7 @@ def _start_rx_task(self) -> None:
log.internal_debug("reception task is not needed, don't start it")
return

log.internal_debug(f"start reception task {self.name}_tx")
log.internal_debug(f"start reception task {self.name}_rx")
self.rx_thread = threading.Thread(
name=f"{self.name}_rx", target=self._reception_task
)
Expand Down
4 changes: 2 additions & 2 deletions src/pykiso/lib/auxiliaries/communication_auxiliary.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def _run_command(self, cmd_message: str, cmd_data: bytes = None) -> bool:
state = False
if cmd_message == "send":
try:
self.channel.cc_send(msg=cmd_data, raw=True)
self.channel.cc_send(msg=cmd_data)
state = True
except Exception:
log.exception(
Expand All @@ -223,7 +223,7 @@ def _receive_message(self, timeout_in_s: float) -> None:
for a message
"""
try:
rcv_data = self.channel.cc_receive(timeout=timeout_in_s, raw=True)
rcv_data = self.channel.cc_receive(timeout=timeout_in_s)
log.internal_debug(f"received message '{rcv_data}' from {self.channel}")
msg = rcv_data.get("msg")
if msg is not None and self.queueing_event.is_set():
Expand Down
Loading

0 comments on commit 869199f

Please sign in to comment.