diff --git a/.release b/.release index 65b19ebeead68eafd0f1ef0a3d5443f62b70dba2..79932ec25b045f4fa1a26bb7d214c401ff78790e 100644 --- a/.release +++ b/.release @@ -1,2 +1,2 @@ -release=0.9.1 -tag=ska_tango_base-0.9.1 +release=0.10.0 +tag=ska_tango_base-0.10.0 diff --git a/README.md b/README.md index 2c9aa5de87321f4cc5fdc3970b864ce4342faff0..cc34052ba2f246dc5ba2b47ac31a700ffd0237fb 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,15 @@ The ska-tango-base repository includes a set of eight classes as mentioned in SK ## Version History +#### 0.10.0 +- Add `DebugDevice` command to `SKABaseDevice`. This allows remote debugging to be + enabled on all devices. It cannot be disabled without restarting the process. + If there are multiple devices in a device server, debugging is only enabled for + the requested device (i.e., methods patched for debugging cppTango threads). + However, all Python threads (not cppTango threads), will also be debuggable, + even if created by devices other than the one that was used to enable debugging. + There is only one debugger instance shared by the whole process. + #### 0.9.1 - Changed dependency from `ska_logging` to `ska_ser_logging`. diff --git a/docs/source/conf.py b/docs/source/conf.py index 7f7c596a2beed92397246b80072796cdf30c7851..361c6f7493eddae7dff276f55c9957e6636776f8 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -56,10 +56,11 @@ _MockObject.__call__ = call_mock # hack end autodoc_mock_imports = [ + 'debugpy', + 'numpy', + 'ska_ser_logging', 'tango', 'transitions', - 'ska_ser_logging', - 'numpy', ] autodoc_default_options = { diff --git a/pogo/CspSubElementMaster.xmi b/pogo/CspSubElementMaster.xmi index ce1f323152605e7a52c54aa96805d639b5479634..c6dfe00a341fa364b2ce81eee038bdba4d885539 100644 --- a/pogo/CspSubElementMaster.xmi +++ b/pogo/CspSubElementMaster.xmi @@ -93,6 +93,15 @@ </argout> <status abstract="false" inherited="true" concrete="true"/> </commands> + <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> + <argin description=""> + <type xsi:type="pogoDsl:VoidType"/> + </argin> + <argout description="TCP port debugger is listening on"> + <type xsi:type="pogoDsl:UShortType"/> + </argout> + <status abstract="false" inherited="true" concrete="true"/> + </commands> <commands name="LoadFirmware" description="Deploy new versions of software and firmware and 
trigger a restart so that a Component initializes using a 
newly deployed version." execMethod="load_firmware" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> <argin description="The file name or a pointer to the filename , 
the list of components that use software or firmware package (file),
checksum or signing"> <type xsi:type="pogoDsl:StringArrayType"/> diff --git a/pogo/CspSubElementObsDevice.xmi b/pogo/CspSubElementObsDevice.xmi index b13e945a1c9d132521f4d9396c2fb16ff5fe818c..217ebab8475db969d99302381d65493270ff8267 100644 --- a/pogo/CspSubElementObsDevice.xmi +++ b/pogo/CspSubElementObsDevice.xmi @@ -66,6 +66,15 @@ </argout> <status abstract="false" inherited="true" concrete="true"/> </commands> + <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> + <argin description=""> + <type xsi:type="pogoDsl:VoidType"/> + </argin> + <argout description="TCP port debugger is listening on"> + <type xsi:type="pogoDsl:UShortType"/> + </argout> + <status abstract="false" inherited="true" concrete="true"/> + </commands> <commands name="ConfigureScan" description="Configure the observing device parameters for the current scan," execMethod="configure_scan" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> <argin description="JSON formatted string with the scan configuration."> <type xsi:type="pogoDsl:StringType"/> diff --git a/pogo/CspSubElementSubarray.xmi b/pogo/CspSubElementSubarray.xmi index fae1774ea21a158f13819d526fe227c4a17000bc..6b6b9b8233f4c6936734dda50c207300ee2f978b 100644 --- a/pogo/CspSubElementSubarray.xmi +++ b/pogo/CspSubElementSubarray.xmi @@ -132,6 +132,15 @@ </argout> <status abstract="false" inherited="true" concrete="true"/> </commands> + <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> + <argin description=""> + <type xsi:type="pogoDsl:VoidType"/> + </argin> + <argout description="TCP port debugger is listening on"> + <type xsi:type="pogoDsl:UShortType"/> + </argout> + <status abstract="false" inherited="true" concrete="true"/> + </commands> <commands name="ObsReset" description="Reset observation state machine to its default state" execMethod="obs_reset" displayLevel="OPERATOR" polledPeriod="0"> <argin description=""> <type xsi:type="pogoDsl:VoidType"/> diff --git a/pogo/SKAAlarmHandler.xmi b/pogo/SKAAlarmHandler.xmi index 5e46b69d59ecc4ee8f8af98968cf56194bd2a88d..d66d34d008ab6022e95a9da57cd966b5d531e002 100644 --- a/pogo/SKAAlarmHandler.xmi +++ b/pogo/SKAAlarmHandler.xmi @@ -104,6 +104,15 @@ </argout> <status abstract="false" inherited="true" concrete="true" concreteHere="false"/> </commands> + <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> + <argin description=""> + <type xsi:type="pogoDsl:VoidType"/> + </argin> + <argout description="TCP port debugger is listening on"> + <type xsi:type="pogoDsl:UShortType"/> + </argout> + <status abstract="false" inherited="true" concrete="true"/> + </commands> <commands name="GetVersionInfo" description="Array of version strings of all entities modelled by this device. 
(One level down only)
Each string in the array lists the version info for one entity
managed by this device. 
The first entry is version info for this TANGO Device itself.
The entities may be TANGO devices, or hardware LRUs or 
anything else this devices manages/models.
The intention with this command is that it can provide more 
detailed information than can be captured in the versionId 
and buildState attributes, if necessary.
In the minimal case the GetVersionInfo will contain only the 
versionId and buildState attributes of the next lower level
entities." execMethod="get_version_info" displayLevel="OPERATOR" polledPeriod="0"> <argin description=""> <type xsi:type="pogoDsl:VoidType"/> diff --git a/pogo/SKABaseDevice.xmi b/pogo/SKABaseDevice.xmi index 2678ee5918496c035f69c983bfa0fc2b2a884617..dec232f5082926c656a64ab642c186dfe07d91ff 100644 --- a/pogo/SKABaseDevice.xmi +++ b/pogo/SKABaseDevice.xmi @@ -59,6 +59,15 @@ </argout> <status abstract="false" inherited="false" concrete="true" concreteHere="true"/> </commands> + <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> + <argin description=""> + <type xsi:type="pogoDsl:VoidType"/> + </argin> + <argout description="TCP port debugger is listening on"> + <type xsi:type="pogoDsl:UShortType"/> + </argout> + <status abstract="false" inherited="false" concrete="true" concreteHere="true"/> + </commands> <attributes name="buildState" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true" isDynamic="false"> <dataType xsi:type="pogoDsl:StringType"/> <dataReadyEvent fire="false" libCheckCriteria="true"/> diff --git a/pogo/SKACapability.xmi b/pogo/SKACapability.xmi index 2a12b45be7b1ccd32ed2b4e9fe1f3e94f26b2ad1..824d079a622c53f84d072c48a429ef86ee566eae 100644 --- a/pogo/SKACapability.xmi +++ b/pogo/SKACapability.xmi @@ -81,6 +81,15 @@ </argout> <status abstract="false" inherited="true" concrete="true"/> </commands> + <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> + <argin description=""> + <type xsi:type="pogoDsl:VoidType"/> + </argin> + <argout description="TCP port debugger is listening on"> + <type xsi:type="pogoDsl:UShortType"/> + </argout> + <status abstract="false" inherited="true" concrete="true"/> + </commands> <attributes name="activationTime" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true" isDynamic="false"> <dataType xsi:type="pogoDsl:DoubleType"/> <changeEvent fire="false" libCheckCriteria="false"/> diff --git a/pogo/SKALogger.xmi b/pogo/SKALogger.xmi index d61abf2f20dd94cfa3d5f369f326c8a73cc0830a..b1531989dabd7b95b1accbe272f2b10c9ff5b63a 100644 --- a/pogo/SKALogger.xmi +++ b/pogo/SKALogger.xmi @@ -69,6 +69,15 @@ </argout> <status abstract="false" inherited="true" concrete="true"/> </commands> + <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> + <argin description=""> + <type xsi:type="pogoDsl:VoidType"/> + </argin> + <argout description="TCP port debugger is listening on"> + <type xsi:type="pogoDsl:UShortType"/> + </argout> + <status abstract="false" inherited="true" concrete="true"/> + </commands> <attributes name="buildState" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true"> <dataType xsi:type="pogoDsl:StringType"/> <status abstract="false" inherited="true" concrete="true"/> diff --git a/pogo/SKAMaster.xmi b/pogo/SKAMaster.xmi index 2862a87a3f36ce3ca1de6d0d818fa2e325f4431c..8463630a2e3a6ebea32856698e3296e23632f93c 100644 --- a/pogo/SKAMaster.xmi +++ b/pogo/SKAMaster.xmi @@ -82,6 +82,15 @@ </argout> <status abstract="false" inherited="true" concrete="true"/> </commands> + <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> + <argin description=""> + <type xsi:type="pogoDsl:VoidType"/> + </argin> + <argout description="TCP port debugger is listening on"> + <type xsi:type="pogoDsl:UShortType"/> + </argout> + <status abstract="false" inherited="true" concrete="true"/> + </commands> <attributes name="elementLoggerAddress" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true" isDynamic="false"> <dataType xsi:type="pogoDsl:StringType"/> <changeEvent fire="false" libCheckCriteria="false"/> diff --git a/pogo/SKAObsDevice.xmi b/pogo/SKAObsDevice.xmi index ca0feba3a37b6f97eec259d79e7ce8fc02dbec93..d10090156d372e8032ac19abaf881c70b8a0fb35 100644 --- a/pogo/SKAObsDevice.xmi +++ b/pogo/SKAObsDevice.xmi @@ -60,6 +60,15 @@ </argout> <status abstract="false" inherited="true" concrete="true"/> </commands> + <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> + <argin description=""> + <type xsi:type="pogoDsl:VoidType"/> + </argin> + <argout description="TCP port debugger is listening on"> + <type xsi:type="pogoDsl:UShortType"/> + </argout> + <status abstract="false" inherited="true" concrete="true"/> + </commands> <attributes name="obsState" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true" isDynamic="false"> <dataType xsi:type="pogoDsl:EnumType"/> <changeEvent fire="true" libCheckCriteria="true"/> diff --git a/pogo/SKASubarray.xmi b/pogo/SKASubarray.xmi index e5a277d64ece861a7040200061bec9e723a9cbf6..b5e5e06f3f1daf79a8816028b09d8c4a0cb295b3 100644 --- a/pogo/SKASubarray.xmi +++ b/pogo/SKASubarray.xmi @@ -131,6 +131,15 @@ </argout> <status abstract="false" inherited="true" concrete="true" concreteHere="false"/> </commands> + <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> + <argin description=""> + <type xsi:type="pogoDsl:VoidType"/> + </argin> + <argout description="TCP port debugger is listening on"> + <type xsi:type="pogoDsl:UShortType"/> + </argout> + <status abstract="false" inherited="true" concrete="true"/> + </commands> <commands name="ObsReset" description="Reset observation state machine to its default state" execMethod="obsreset" displayLevel="OPERATOR" polledPeriod="0"> <argin description=""> <type xsi:type="pogoDsl:VoidType"/> diff --git a/pogo/SKATelState.xmi b/pogo/SKATelState.xmi index 39cbae8303c738ca75d78a6e9ac3fcca8cba30d6..4c3e3142d043862a8fa27b2ebb57433150eaa47d 100644 --- a/pogo/SKATelState.xmi +++ b/pogo/SKATelState.xmi @@ -64,6 +64,15 @@ </argout> <status abstract="false" inherited="true" concrete="true"/> </commands> + <commands name="DebugDevice" description="Enables remote debugging of this device" execMethod="debug_device" displayLevel="OPERATOR" polledPeriod="0" isDynamic="false"> + <argin description=""> + <type xsi:type="pogoDsl:VoidType"/> + </argin> + <argout description="TCP port debugger is listening on"> + <type xsi:type="pogoDsl:UShortType"/> + </argout> + <status abstract="false" inherited="true" concrete="true"/> + </commands> <attributes name="buildState" attType="Scalar" rwType="READ" displayLevel="OPERATOR" polledPeriod="0" maxX="" maxY="" allocReadMember="true"> <dataType xsi:type="pogoDsl:StringType"/> <status abstract="false" inherited="true" concrete="true"/> diff --git a/setup.py b/setup.py index 8a1b17553ac2e5d54f97ab95a1a0587a7a79172c..a4fb47cb92b9249d832b3cb59c4b5ceb4d1365cc 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ setuptools.setup( ], platforms=["OS Independent"], setup_requires=[] + pytest_runner, - install_requires=["transitions", "ska_ser_logging"], + install_requires=["debugpy", "transitions", "ska_ser_logging"], tests_require=["pytest", "coverage", "pytest-json-report", "pytest-forked"], entry_points={ "console_scripts": [ diff --git a/src/ska_tango_base/base_device.py b/src/ska_tango_base/base_device.py index b78fac15264bcd25956f8e38a14109c8310e10ea..008e1f8bcde97ffc8d4246678e10bb915da41a74 100644 --- a/src/ska_tango_base/base_device.py +++ b/src/ska_tango_base/base_device.py @@ -13,13 +13,16 @@ device. # PROTECTED REGION ID(SKABaseDevice.additionnal_import) ENABLED START # # Standard imports import enum +import inspect import logging import logging.handlers import socket import sys import threading +import typing import warnings -from transitions import MachineError + +from functools import partial from urllib.parse import urlparse from urllib.request import url2pathname @@ -28,6 +31,7 @@ from tango import AttrWriteType, DebugIt, DevState from tango.server import run, Device, attribute, command, device_property # SKA specific imports +import debugpy import ska_ser_logging from ska_tango_base import release from ska_tango_base.commands import ( @@ -44,6 +48,7 @@ from ska_tango_base.utils import get_groups_from_json, for_testing_only from ska_tango_base.faults import GroupDefinitionsError, LoggingTargetError, LoggingLevelError LOG_FILE_SIZE = 1024 * 1024 # Log file size 1MB. +_DEBUGGER_PORT = 5678 class _Log4TangoLoggingLevel(enum.IntEnum): @@ -558,6 +563,8 @@ class SKABaseDevice(Device): A generic base device for SKA. """ + _global_debugger_listening = False + class InitCommand(ActionCommand): """ A class for the SKABaseDevice's init_device() "command". @@ -611,6 +618,7 @@ class SKABaseDevice(Device): release.version, release.description) device._version_id = release.version + device._methods_patched_for_debugger = False try: # create TANGO Groups dict, according to property @@ -1002,6 +1010,7 @@ class SKABaseDevice(Device): self.register_command_object( "GetVersionInfo", self.GetVersionInfoCommand(*device_args) ) + self.register_command_object("DebugDevice", self.DebugDeviceCommand(*device_args)) def always_executed_hook(self): # PROTECTED REGION ID(SKABaseDevice.always_executed_hook) ENABLED START # @@ -1629,6 +1638,112 @@ class SKABaseDevice(Device): (return_code, message) = command() return [[return_code], [message]] + class DebugDeviceCommand(BaseCommand): + """ + A class for the SKABaseDevice's DebugDevice() command. + """ + + def do(self): + """ + Stateless hook for device DebugDevice() command. + + Starts the ``debugpy`` debugger listening for remote connections + (via Debugger Adaptor Protocol), and patches all methods so that + they can be debugged. + + If the debugger is already listening, additional execution of this + command will trigger a breakpoint. + + :return: The TCP port the debugger is listening on. + :rtype: DevUShort + """ + if not SKABaseDevice._global_debugger_listening: + self.start_debugger(_DEBUGGER_PORT) + SKABaseDevice._global_debugger_listening = True + device = self.target + if not device._methods_patched_for_debugger: + self.monkey_patch_all_methods_for_debugger() + device._methods_patched_for_debugger = True + else: + self.logger.warning("Triggering debugger breakpoint...") + debugpy.breakpoint() + return _DEBUGGER_PORT + + def start_debugger(self, port): + self.logger.warning("Starting debugger...") + debugpy.listen(("0.0.0.0", port)) + self.logger.warning( + f"Debugger listening on port {port}. Performance may be degraded." + ) + + def monkey_patch_all_methods_for_debugger(self): + all_methods = self.get_all_methods() + patched = [] + for owner, name, method in all_methods: + if self.method_must_be_patched_for_debugger(owner, method): + self.patch_method_for_debugger(owner, name, method) + patched.append( + f"{owner} {method.__func__.__qualname__} in {method.__func__.__module__}" + ) + self.logger.info("Patched %s of %s methods", len(patched), len(all_methods)) + self.logger.debug("Patched methods: %s", sorted(patched)) + + def get_all_methods(self): + methods = [] + device = self.target + for name, method in inspect.getmembers(device, inspect.ismethod): + methods.append((device, name, method)) + for command_object in device._command_objects.values(): + for name, method in inspect.getmembers(command_object, inspect.ismethod): + methods.append((command_object, name, method)) + return methods + + @staticmethod + def method_must_be_patched_for_debugger(owner, method): + """Determine if methods are worth debugging. + + The goal is to find all the user's Python methods, but not the + lower level PyTango device and Boost extension methods. The + `typing.types.FunctionType` check excludes the Boost methods. + """ + skip_module_names = ["tango.device_server", "tango.server", "logging"] + skip_owner_types = [SKABaseDevice.DebugDeviceCommand] + return ( + isinstance(method.__func__, typing.types.FunctionType) + and method.__func__.__module__ not in skip_module_names + and type(owner) not in skip_owner_types + ) + + def patch_method_for_debugger(self, owner, name, method): + """Ensure method calls trigger the debugger. + + Most methods in a device are executed by calls from threads spawned + by the cppTango layer. These threads are not known to Python, so + we have to explicitly inform the debugger about them. + """ + + def debug_thread_wrapper(orig_method, *args, **kwargs): + debugpy.debug_this_thread() + return orig_method(*args, **kwargs) + + patched_method = partial(debug_thread_wrapper, method) + setattr(owner, name, patched_method) + + @command( + dtype_out="DevUShort", + doc_out="The TCP port the debugger is listening on." + ) + @DebugIt() + def DebugDevice(self): + """ + Enables remote debugging of this device. + + To modify behaviour for this command, modify the do() method of + the command class: :class:`.DebugDeviceCommand`. + """ + command = self.get_command_object("DebugDevice") + return command() + # ---------- # Run server diff --git a/src/ska_tango_base/release.py b/src/ska_tango_base/release.py index a3970d425784bca074cc7697719b5167fabff71f..6e60c36d97062f11c2303d32e4d1f2b65093a31e 100644 --- a/src/ska_tango_base/release.py +++ b/src/ska_tango_base/release.py @@ -7,7 +7,7 @@ """Release information for ska_tango_base Python Package""" name = """ska_tango_base""" -version = "0.9.1" +version = "0.10.0" version_info = version.split(".") description = """A set of generic base devices for SKA Telescope.""" author = "SKA India and SARAO and CSIRO and INAF" diff --git a/tests/test_alarm_handler_device.py b/tests/test_alarm_handler_device.py index 9675262b3f14c19b0475c93377a904c13dc62853..3757d8b512f632be1ab005176fb98eb73abbba34 100644 --- a/tests/test_alarm_handler_device.py +++ b/tests/test_alarm_handler_device.py @@ -90,7 +90,7 @@ class TestSKAAlarmHandler(object): """Test for GetVersionInfo""" # PROTECTED REGION ID(SKAAlarmHandler.test_GetVersionInfo) ENABLED START # versionPattern = re.compile( - r'SKAAlarmHandler, ska_tango_base, [0-9].[0-9].[0-9], ' + r'SKAAlarmHandler, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope.') versionInfo = tango_context.device.GetVersionInfo() assert (re.match(versionPattern, versionInfo[0])) is not None @@ -142,7 +142,7 @@ class TestSKAAlarmHandler(object): """Test for buildState""" # PROTECTED REGION ID(SKAAlarmHandler.test_buildState) ENABLED START # buildPattern = re.compile( - r'ska_tango_base, [0-9].[0-9].[0-9], ' + r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope') assert (re.match(buildPattern, tango_context.device.buildState)) is not None # PROTECTED REGION END # // SKAAlarmHandler.test_buildState @@ -152,7 +152,7 @@ class TestSKAAlarmHandler(object): def test_versionId(self, tango_context): """Test for versionId""" # PROTECTED REGION ID(SKAAlarmHandler.test_versionId) ENABLED START # - versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]') + versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+') assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None # PROTECTED REGION END # // SKAAlarmHandler.test_versionId diff --git a/tests/test_base_device.py b/tests/test_base_device.py index 896f6e12eb6b2c24ffe88843aa066d5f2717ecde..5e79d03e92440e88dd63e2d0de5505a44f17bd0b 100644 --- a/tests/test_base_device.py +++ b/tests/test_base_device.py @@ -25,6 +25,7 @@ from ska_tango_base.control_model import ( AdminMode, ControlMode, HealthState, LoggingLevel, SimulationMode, TestMode ) from ska_tango_base.base_device import ( + _DEBUGGER_PORT, _Log4TangoLoggingLevel, _PYTHON_TO_TANGO_LOGGING_LEVEL, LoggingUtils, @@ -425,7 +426,7 @@ class TestSKABaseDevice(object): """Test for GetVersionInfo""" # PROTECTED REGION ID(SKABaseDevice.test_GetVersionInfo) ENABLED START # versionPattern = re.compile( - r'SKABaseDevice, ska_tango_base, [0-9].[0-9].[0-9], ' + r'SKABaseDevice, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope.') versionInfo = tango_context.device.GetVersionInfo() assert (re.match(versionPattern, versionInfo[0])) is not None @@ -508,7 +509,7 @@ class TestSKABaseDevice(object): """Test for buildState""" # PROTECTED REGION ID(SKABaseDevice.test_buildState) ENABLED START # buildPattern = re.compile( - r'ska_tango_base, [0-9].[0-9].[0-9], ' + r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope') assert (re.match(buildPattern, tango_context.device.buildState)) is not None # PROTECTED REGION END # // SKABaseDevice.test_buildState @@ -518,7 +519,7 @@ class TestSKABaseDevice(object): def test_versionId(self, tango_context): """Test for versionId""" # PROTECTED REGION ID(SKABaseDevice.test_versionId) ENABLED START # - versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]') + versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+') assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None # PROTECTED REGION END # // SKABaseDevice.test_versionId @@ -649,6 +650,31 @@ class TestSKABaseDevice(object): assert tango_context.device.testMode == TestMode.NONE # PROTECTED REGION END # // SKABaseDevice.test_testMode + def test_debugger_not_listening_by_default(self, tango_context): + assert not SKABaseDevice._global_debugger_listening + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + with pytest.raises(ConnectionRefusedError): + s.connect(("localhost", _DEBUGGER_PORT)) + + def test_DebugDevice_starts_listening(self, tango_context): + port = tango_context.device.DebugDevice() + assert port == _DEBUGGER_PORT + assert SKABaseDevice._global_debugger_listening + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.connect(("localhost", _DEBUGGER_PORT)) + assert tango_context.device.state + + def test_DebugDevice_twice_does_not_raise(self, tango_context): + tango_context.device.DebugDevice() + tango_context.device.DebugDevice() + assert SKABaseDevice._global_debugger_listening + + def test_DebugDevice_does_not_break_a_command(self, tango_context): + tango_context.device.DebugDevice() + assert tango_context.device.State() == DevState.OFF + tango_context.device.On() + assert tango_context.device.State() == DevState.ON + class TestSKABaseDevice_commands: """ diff --git a/tests/test_capability_device.py b/tests/test_capability_device.py index 6c9820931227d6d9a58c94b2411de481054e94a9..fdc287abcf734f3446f9121815054a933c47cdee 100644 --- a/tests/test_capability_device.py +++ b/tests/test_capability_device.py @@ -88,7 +88,7 @@ class TestSKACapability(object): """Test for buildState""" # PROTECTED REGION ID(SKACapability.test_buildState) ENABLED START # buildPattern = re.compile( - r'ska_tango_base, [0-9].[0-9].[0-9], ' + r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope') assert (re.match(buildPattern, tango_context.device.buildState)) is not None # PROTECTED REGION END # // SKACapability.test_buildState @@ -98,7 +98,7 @@ class TestSKACapability(object): def test_versionId(self, tango_context): """Test for versionId""" # PROTECTED REGION ID(SKACapability.test_versionId) ENABLED START # - versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]') + versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+') assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None # PROTECTED REGION END # // SKACapability.test_versionId diff --git a/tests/test_csp_subelement_master.py b/tests/test_csp_subelement_master.py index 6ffaf7eaaa1fdfbcd84799063d9da58632bb128d..694fb89360629784d7a769e189c3cd9eec07281a 100644 --- a/tests/test_csp_subelement_master.py +++ b/tests/test_csp_subelement_master.py @@ -75,7 +75,7 @@ class TestCspSubElementMaster(object): """Test for GetVersionInfo""" # PROTECTED REGION ID(CspSubelementMaster.test_GetVersionInfo) ENABLED START # versionPattern = re.compile( - r'CspSubElementMaster, ska_tango_base, [0-9].[0-9].[0-9], ' + r'CspSubElementMaster, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope.') versionInfo = tango_context.device.GetVersionInfo() assert (re.match(versionPattern, versionInfo[0])) is not None @@ -86,7 +86,7 @@ class TestCspSubElementMaster(object): """Test for buildState""" # PROTECTED REGION ID(CspSubelementMaster.test_buildState) ENABLED START # buildPattern = re.compile( - r'ska_tango_base, [0-9].[0-9].[0-9], ' + r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope') assert (re.match(buildPattern, tango_context.device.buildState)) is not None # PROTECTED REGION END # // CspSubelementMaster.test_buildState @@ -96,7 +96,7 @@ class TestCspSubElementMaster(object): def test_versionId(self, tango_context): """Test for versionId""" # PROTECTED REGION ID(CspSubelementMaster.test_versionId) ENABLED START # - versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]') + versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+') assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None # PROTECTED REGION END # // CspSubelementMaster.test_versionId diff --git a/tests/test_csp_subelement_obsdevice.py b/tests/test_csp_subelement_obsdevice.py index 971a7f44cf54a467d331c8e5d0f21f6fbd8a35e6..963b889b9e271396ed7d8d483faf8121a6b61d45 100644 --- a/tests/test_csp_subelement_obsdevice.py +++ b/tests/test_csp_subelement_obsdevice.py @@ -102,7 +102,7 @@ class TestCspSubElementObsDevice(object): """Test for GetVersionInfo""" # PROTECTED REGION ID(CspSubelementObsDevice.test_GetVersionInfo) ENABLED START # versionPattern = re.compile( - r'CspSubElementObsDevice, ska_tango_base, [0-9].[0-9].[0-9], ' + r'CspSubElementObsDevice, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope.') versionInfo = tango_context.device.GetVersionInfo() assert (re.match(versionPattern, versionInfo[0])) is not None @@ -114,7 +114,7 @@ class TestCspSubElementObsDevice(object): """Test for buildState""" # PROTECTED REGION ID(CspSubelementObsDevice.test_buildState) ENABLED START # buildPattern = re.compile( - r'ska_tango_base, [0-9].[0-9].[0-9], ' + r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope') assert (re.match(buildPattern, tango_context.device.buildState)) is not None # PROTECTED REGION END # // CspSubelementObsDevice.test_buildState @@ -124,7 +124,7 @@ class TestCspSubElementObsDevice(object): def test_versionId(self, tango_context): """Test for versionId""" # PROTECTED REGION ID(CspSubelementObsDevice.test_versionId) ENABLED START # - versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]') + versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+') assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None # PROTECTED REGION END # // CspSubelementObsDevice.test_versionId diff --git a/tests/test_csp_subelement_subarray.py b/tests/test_csp_subelement_subarray.py index 1418a88854ef8b74d4d00e2e1cdcbee6408e29cb..fd5ca072adba85855fcdbdf993d99dc1e05801a1 100644 --- a/tests/test_csp_subelement_subarray.py +++ b/tests/test_csp_subelement_subarray.py @@ -78,7 +78,7 @@ class TestCspSubElementSubarray(object): """Test for GetVersionInfo""" # PROTECTED REGION ID(CspSubelementSubarray.test_GetVersionInfo) ENABLED START # versionPattern = re.compile( - r'CspSubElementSubarray, ska_tango_base, [0-9].[0-9].[0-9], ' + r'CspSubElementSubarray, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope.') versionInfo = tango_context.device.GetVersionInfo() assert (re.match(versionPattern, versionInfo[0])) is not None @@ -89,7 +89,7 @@ class TestCspSubElementSubarray(object): """Test for buildState""" # PROTECTED REGION ID(CspSubelementSubarray.test_buildState) ENABLED START # buildPattern = re.compile( - r'ska_tango_base, [0-9].[0-9].[0-9], ' + r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope') assert (re.match(buildPattern, tango_context.device.buildState)) is not None # PROTECTED REGION END # // CspSubelementSubarray.test_buildState @@ -99,7 +99,7 @@ class TestCspSubElementSubarray(object): def test_versionId(self, tango_context): """Test for versionId""" # PROTECTED REGION ID(CspSubelementSubarray.test_versionId) ENABLED START # - versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]') + versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+') assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None # PROTECTED REGION END # // CspSubelementSubarray.test_versionId diff --git a/tests/test_logger_device.py b/tests/test_logger_device.py index 25692affbe73ffa587d768d41c4d3abf6647dae4..f035c46833979642f7f920b4dfe3cde98f76319a 100644 --- a/tests/test_logger_device.py +++ b/tests/test_logger_device.py @@ -76,7 +76,7 @@ class TestSKALogger(object): """Test for GetVersionInfo""" # PROTECTED REGION ID(SKALogger.test_GetVersionInfo) ENABLED START # versionPattern = re.compile( - r"SKALogger, ska_tango_base, [0-9].[0-9].[0-9], " + r"SKALogger, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, " r"A set of generic base devices for SKA Telescope." ) versionInfo = tango_context.device.GetVersionInfo() @@ -89,7 +89,7 @@ class TestSKALogger(object): """Test for buildState""" # PROTECTED REGION ID(SKALogger.test_buildState) ENABLED START # buildPattern = re.compile( - r"ska_tango_base, [0-9].[0-9].[0-9], " + r"ska_tango_base, [0-9]+.[0-9]+.[0-9]+, " r"A set of generic base devices for SKA Telescope" ) assert (re.match(buildPattern, tango_context.device.buildState)) is not None @@ -100,7 +100,7 @@ class TestSKALogger(object): def test_versionId(self, tango_context): """Test for versionId""" # PROTECTED REGION ID(SKALogger.test_versionId) ENABLED START # - versionIdPattern = re.compile(r"[0-9].[0-9].[0-9]") + versionIdPattern = re.compile(r"[0-9]+.[0-9]+.[0-9]+") assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None # PROTECTED REGION END # // SKALogger.test_versionId diff --git a/tests/test_master_device.py b/tests/test_master_device.py index 30e3b7cf6315f8e2912cb09123c0a2d10286763c..a9c9b45d23064fcdbd3361c09eed096171a01ef7 100644 --- a/tests/test_master_device.py +++ b/tests/test_master_device.py @@ -69,7 +69,7 @@ class TestSKAMaster(object): """Test for GetVersionInfo""" # PROTECTED REGION ID(SKAMaster.test_GetVersionInfo) ENABLED START # versionPattern = re.compile( - r'SKAMaster, ska_tango_base, [0-9].[0-9].[0-9], ' + r'SKAMaster, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope.') versionInfo = tango_context.device.GetVersionInfo() assert (re.match(versionPattern, versionInfo[0])) is not None @@ -129,7 +129,7 @@ class TestSKAMaster(object): """Test for buildState""" # PROTECTED REGION ID(SKAMaster.test_buildState) ENABLED START # buildPattern = re.compile( - r'ska_tango_base, [0-9].[0-9].[0-9], ' + r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope') assert ( re.match(buildPattern, tango_context.device.buildState) @@ -141,7 +141,7 @@ class TestSKAMaster(object): def test_versionId(self, tango_context): """Test for versionId""" # PROTECTED REGION ID(SKAMaster.test_versionId) ENABLED START # - versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]') + versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+') assert ( re.match(versionIdPattern, tango_context.device.versionId) ) is not None diff --git a/tests/test_obs_device.py b/tests/test_obs_device.py index d6d8cbfa570f6d128bcb0cbb5aad78c461321c5e..7a6c230309142c3f35c013c72efd9af05c041064 100644 --- a/tests/test_obs_device.py +++ b/tests/test_obs_device.py @@ -72,7 +72,7 @@ class TestSKAObsDevice(object): """Test for GetVersionInfo""" # PROTECTED REGION ID(SKAObsDevice.test_GetVersionInfo) ENABLED START # versionPattern = re.compile( - r'SKAObsDevice, ska_tango_base, [0-9].[0-9].[0-9], ' + r'SKAObsDevice, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope.') versionInfo = tango_context.device.GetVersionInfo() assert (re.match(versionPattern, versionInfo[0])) is not None @@ -121,7 +121,7 @@ class TestSKAObsDevice(object): """Test for buildState""" # PROTECTED REGION ID(SKAObsDevice.test_buildState) ENABLED START # buildPattern = re.compile( - r'ska_tango_base, [0-9].[0-9].[0-9], ' + r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope') assert (re.match(buildPattern, tango_context.device.buildState)) is not None # PROTECTED REGION END # // SKAObsDevice.test_buildState @@ -131,7 +131,7 @@ class TestSKAObsDevice(object): def test_versionId(self, tango_context): """Test for versionId""" # PROTECTED REGION ID(SKAObsDevice.test_versionId) ENABLED START # - versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]') + versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+') assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None # PROTECTED REGION END # // SKAObsDevice.test_versionId diff --git a/tests/test_subarray_device.py b/tests/test_subarray_device.py index 9c817363495653ef73b9f6974ef9e6a4eab7bb56..8a1c49aaa5c0c5c1dc6652aa012a519fb43d2224 100644 --- a/tests/test_subarray_device.py +++ b/tests/test_subarray_device.py @@ -146,7 +146,7 @@ class TestSKASubarray: """Test for GetVersionInfo""" # PROTECTED REGION ID(SKASubarray.test_GetVersionInfo) ENABLED START # versionPattern = re.compile( - r'SKASubarray, ska_tango_base, [0-9].[0-9].[0-9], ' + r'SKASubarray, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope.') versionInfo = tango_context.device.GetVersionInfo() assert (re.match(versionPattern, versionInfo[0])) is not None @@ -359,7 +359,7 @@ class TestSKASubarray: """Test for buildState""" # PROTECTED REGION ID(SKASubarray.test_buildState) ENABLED START # buildPattern = re.compile( - r'ska_tango_base, [0-9].[0-9].[0-9], ' + r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope') assert (re.match(buildPattern, tango_context.device.buildState)) is not None # PROTECTED REGION END # // SKASubarray.test_buildState @@ -433,7 +433,7 @@ class TestSKASubarray: def test_versionId(self, tango_context): """Test for versionId""" # PROTECTED REGION ID(SKASubarray.test_versionId) ENABLED START # - versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]') + versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+') assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None # PROTECTED REGION END # // SKASubarray.test_versionId diff --git a/tests/test_tel_state_device.py b/tests/test_tel_state_device.py index 0e3b719c59a9987c2abcc4142ee7761682737a23..6f3eb7d8d559318cc6e7230d5ddc24424c1230e5 100644 --- a/tests/test_tel_state_device.py +++ b/tests/test_tel_state_device.py @@ -66,7 +66,7 @@ class TestSKATelState(object): """Test for GetVersionInfo""" # PROTECTED REGION ID(SKATelState.test_GetVersionInfo) ENABLED START # versionPattern = re.compile( - r'SKATelState, ska_tango_base, [0-9].[0-9].[0-9], ' + r'SKATelState, ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope.') versionInfo = tango_context.device.GetVersionInfo() assert (re.match(versionPattern, versionInfo[0])) is not None @@ -78,7 +78,7 @@ class TestSKATelState(object): """Test for buildState""" # PROTECTED REGION ID(SKATelState.test_buildState) ENABLED START # buildPattern = re.compile( - r'ska_tango_base, [0-9].[0-9].[0-9], ' + r'ska_tango_base, [0-9]+.[0-9]+.[0-9]+, ' r'A set of generic base devices for SKA Telescope') assert (re.match(buildPattern, tango_context.device.buildState)) is not None # PROTECTED REGION END # // SKATelState.test_buildState @@ -88,7 +88,7 @@ class TestSKATelState(object): def test_versionId(self, tango_context): """Test for versionId""" # PROTECTED REGION ID(SKATelState.test_versionId) ENABLED START # - versionIdPattern = re.compile(r'[0-9].[0-9].[0-9]') + versionIdPattern = re.compile(r'[0-9]+.[0-9]+.[0-9]+') assert (re.match(versionIdPattern, tango_context.device.versionId)) is not None # PROTECTED REGION END # // SKATelState.test_versionId