From d4729cd390037b2cba0ffe9b410db90a1ceed289 Mon Sep 17 00:00:00 2001 From: Gianluca Marotta <gianluca.marotta@inaf.it> Date: Mon, 21 Dec 2020 19:53:18 +0100 Subject: [PATCH] CT-206 Test that the transaction id is captured in log when applied to Configure and Assign Resources --- .../csp_lmc_common/utils/test_utils.py | 67 +++++++++++++++++ .../tests/unit/cspsubarray_unit_test.py | 73 ++++++++++++++++++- 2 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 csp-lmc-common/csp_lmc_common/utils/test_utils.py diff --git a/csp-lmc-common/csp_lmc_common/utils/test_utils.py b/csp-lmc-common/csp_lmc_common/utils/test_utils.py new file mode 100644 index 0000000..c65e226 --- /dev/null +++ b/csp-lmc-common/csp_lmc_common/utils/test_utils.py @@ -0,0 +1,67 @@ +import time +import logging +import numpy +from tango import DeviceProxy, DevState + +LOGGER = logging.getLogger(__name__) + + +class Timeout: + def __init__(self, duration): + self.endtime = time.time() + duration + + def has_expired(self): + return time.time() > self.endtime + + +class Probe: + def __init__(self, proxy, attr_name, expected_state, message): + """ + """ + self.proxy = proxy + self.attr_name = attr_name + self.expected_state = expected_state + self.message = message + self.current_state = DevState.DISABLE + + def get_attribute(self): + return self.attr_name + + def sample(self): + """ + extract the state of client and store it + """ + device_attr = self.proxy.read_attribute(self.attr_name) + self.current_state = device_attr.value + #LOGGER.info("attr_name: {} current_state:{}".format(self.attr_name, self.current_state)) + + def is_satisfied(self): + """ + Check if the state satisfies this test condition + """ + + if isinstance(self.current_state, numpy.ndarray): + return (self.expected_state == self.current_state).all() + return self.expected_state == self.current_state + + +class Poller: + def __init__(self, timeout, interval): + self.timeout = timeout + self.interval = interval + + def check(self, probe: Probe): + """ + Repeatedly check if the probe is satisfied. + Assert false when the timeout expires. + """ + timer = Timeout(self.timeout) + probe.sample() + while not probe.is_satisfied(): + if timer.has_expired(): + LOGGER.debug("Check Timeout on:{}".format(probe.get_attribute())) + assert False, probe.message + time.sleep(self.interval) + probe.sample() + LOGGER.debug("Check success on: {}".format(probe.get_attribute())) + assert True diff --git a/csp-lmc-common/tests/unit/cspsubarray_unit_test.py b/csp-lmc-common/tests/unit/cspsubarray_unit_test.py index ba6939a..e25e80e 100644 --- a/csp-lmc-common/tests/unit/cspsubarray_unit_test.py +++ b/csp-lmc-common/tests/unit/cspsubarray_unit_test.py @@ -4,13 +4,15 @@ import sys import mock import pytest import tango +import logging +import re from mock import Mock, MagicMock - -from ska.base.control_model import HealthState, ObsState +from ska.base.control_model import HealthState, ObsState, LoggingLevel from ska.base.commands import ResultCode from tango.test_context import DeviceTestContext from tango import DevState, DevFailed from csp_lmc_common.CspSubarray import CspSubarray +from csp_lmc_common.utils.test_utils import Probe, Poller def test_cspsubarray_state_and_obstate_value_after_initialization(): """ @@ -147,6 +149,57 @@ def test_cspsbarray_state_after_On_forwarded_to_subelement_subarray(): assert tango_context.device.State() == DevState.ON assert tango_context.device.obsState == ObsState.EMPTY +def test_cspsubarray_transaction_id_in_log(capsys): + """ + Test that when transaction_id decorator is applied to the Configure + and Assign Resources command, both transaction id are captured in log + """ + device_under_test = CspSubarray + cbf_subarray_fqdn = 'mid_csp_cbf/sub_elt/subarray_01' + pss_subarray_fqdn = 'mid_csp_pss/sub_elt/subarray_01' + cbf_subarray_state_attr = 'obsState' + dut_properties = { + 'CspMaster':'mid_csp/elt/master', + 'CbfSubarray': cbf_subarray_fqdn, + 'PssSubarray': 'mid_csp_pss/sub_elt/subarray_01', + } + event_subscription_map = {} + cbf_subarray_device_proxy_mock = Mock() + pss_subarray_device_proxy_mock = Mock() + proxies_to_mock = { + cbf_subarray_fqdn: cbf_subarray_device_proxy_mock, + pss_subarray_fqdn: pss_subarray_device_proxy_mock, + } + cbf_subarray_device_proxy_mock.subscribe_event.side_effect = ( + lambda attr_name, event_type, callback, *args, **kwargs: event_subscription_map.update({attr_name: callback})) + + with fake_tango_system(device_under_test, initial_dut_properties=dut_properties, proxies_to_mock=proxies_to_mock) as tango_context: + cbf_subarray_device_proxy_mock.On.side_effect = return_ok + pss_subarray_device_proxy_mock.On.side_effect = return_ok + tango_context.device.On() + assert tango_context.device.State() == DevState.ON + tango_context.device.AssignResources('{"example":"band"}') + dummy_event = create_dummy_obs_event(cbf_subarray_fqdn, ObsState.IDLE) + event_subscription_map[cbf_subarray_state_attr](dummy_event) + assert tango_context.device.obsState == ObsState.IDLE + tango_context.device.Configure('{"id":"sbi-400-scienceA"}') + # a prober is needed since the duration of the Configure command is variable. + prober_obs_state = Probe(tango_context.device, "obsState", ObsState.READY, f"Configure command out of time") + Poller(10, 0.1).check(prober_obs_state) + assert_that_log_contains('ConfigureCommand',capsys) + assert_that_log_contains('AssignResourcesCommand', capsys) + +def assert_that_log_contains(name:str,capsys): + patterns = [f'|Transaction.*(?<=Enter\[{name}\])',f'|Transaction.*(?<=Exit\[{name}\])'] + for pattern in patterns: + found = False + out, err = capsys.readouterr() + if re.match(pattern,out): + found = True + break + if not found: + raise AssertionError(f'pattern ({pattern}) not found in expected log messages') + def return_ok(): """ Return a FAILED code in the execution of a device method. @@ -187,6 +240,22 @@ def create_dummy_event(cbf_subarray_fqdn, event_value): fake_event.device.dev_name.side_effect=(lambda *args, **kwargs: mock_event_dev_name(cbf_subarray_fqdn)) return fake_event +def create_dummy_obs_event(cbf_subarray_fqdn, event_value): + """ + Create a mocked event object to test the event callback method + associate to the attribute at subscription. + param: cbf_subarray_fqdn the CBF Subarray FQDN + event_value the expected value + return: the fake event + """ + fake_event = Mock() + fake_event.err = False + fake_event.attr_name = f"{cbf_subarray_fqdn}/obsstate" + fake_event.attr_value.value = event_value + fake_event.attr_value.name = 'obsState' + fake_event.device.name = cbf_subarray_fqdn + fake_event.device.dev_name.side_effect=(lambda *args, **kwargs: mock_event_dev_name(cbf_subarray_fqdn)) + return fake_event @contextlib.contextmanager def fake_tango_system(device_under_test, initial_dut_properties={}, proxies_to_mock={}, device_proxy_import_path='tango.DeviceProxy'): -- GitLab