Restructured the test setup

General test configurations are now separated from specific axis test
definitions.
This commit is contained in:
Stefan Mathis
2025-08-11 12:01:37 +02:00
parent 1482349496
commit 7ab4486748
25 changed files with 62 additions and 59 deletions

View File

@@ -7,23 +7,28 @@ the tests.
The general architecture of the framework is as follows:
- `ioc` contains all files used to create the IOC used in the tests.
- `tests` contains the tests itself in a hierarchical order:
- `tests/.` contains tests and configuration applicable for any motor record-based
- `setup` contains the motor classes, the EPICS communication interface and generalized tests (which are not tied to one specific axis). These tests are arranged in a hierarchical order:
- `tests/.` contains tests and utility functions applicable for any motor record-based
driver.
- `tests/sinqMotor/.` contains tests and configuration applicable for drivers
- `tests/sinqMotor/.` contains tests and utility functions applicable for drivers
based on https://gitea.psi.ch/lin-epics-modules/sinqMotor
- `tests/sinqMotor/turboPmac/.` contains tests and configuration applicable for the
- `tests/sinqMotor/turboPmac/.` contains tests and utility functions applicable for the
sinqMotor-based Turbo PMAC driver https://gitea.psi.ch/lin-epics-modules/turboPmac
- `tests/sinqMotor/masterMacs/.` contains tests and configuration applicable for the
- `tests/sinqMotor/masterMacs/.` contains tests and utility functions applicable for the
sinqMotor-based MasterMACS driver https://gitea.psi.ch/lin-epics-modules/mastermacs
Individual motors at the test instrument are then set up as subfolders of their
respective driver type.
- `setup` contains classes representing the motors and the EPICS communication interface.
- `tests` contains the tests for the individual axes within subfolders of their respective controller folders. For example, the tests for axis "ax1" of the controller "turboPmac1" configured in `ioc/motors/turboPmac1` should be in `tests/turboPmac1/ax1`, the tests for axis "ax2" of the same controller should be in `tests/turboPmac1/ax2` and so on. If an axis folder has a prepended underscore, it is ignored by `runtests` (e.g. the tests in `tests/turboPmac1/_ax3` would be ignored). This can be used to disable tests when e.g. the axis is not available temporarily.
The classes are used to initialize the individual motors within the `tests` directory.
- `config.yaml` is the test configuration (see [Configuration](#configuration))
- `maketestenv` can be used to create a Python virtual environment for running the tests.
- `runtests` is a parallelizing `pytest` wrapper. See section for more information [Parallelizing tests over motors](#parallelizing-tests-over-motors).
## Installation
The following prerequisites need to be fulfilled:
1) EPICS 7 and the IOC shell must be installed.
2) The PSI EPICS drive `/sq_epics/ioc` must be mounted as `/ioc`.
The repository can then be simply cloned anywhere. Depending on the available hardware, the [configuration](#configuration) needs to be adjusted.
## Configuration
The test setup is defined in `config.yaml`. This file contains information which
@@ -33,6 +38,22 @@ identifier of the records created by the motor drivers.
- `versions`: Driver versions used in the IOC
- `controllers`: Configuration of the different motor controllers (IP + port and poll periods)
### Adding / removing a controller to the IOC
The controller name and its configuration needs to added under `controllers` in the following format:
```yaml
turboPmac1:
ip: "172.28.101.24" # Controller IP adress
port: 1025 # Controller port
busypoll: 0.05 # Time in seconds
idlepoll: 1 # Time in seconds
```
Additionally, two files need to be added under `ioc/motors`: `ioc/motors/turboPmac1.cmd` and `ioc/motors/turboPmac1.substitutions`.
These files need to be configured according to the corresponding motor driver documentation (e.g. according to https://gitea.psi.ch/lin-epics-modules/turboPmac for a TurboPMAC driver).
Lastly, `ioc/st.cmd` needs to run the `.cmd` file under motors and to import the driver. See the documentation of [sinqMotor](https://gitea.psi.ch/lin-epics-modules/sinqMotor) for details.
## Starting the IOC
It is recommended to start the IOC via `ioc/startioc`:

View File

@@ -1,6 +1,6 @@
pvprefix: DRVTESTS
versions:
turboPmac: "mathis_s"
turboPmac: "1.3.1"
masterMacs: "1.1"
controllers:
turboPmac1:

View File

@@ -1,28 +1,3 @@
# Configuration for the Turbo PMAC motor controller
#
# Important functions:
#
# turboPmacController:
# Creates the controller object and specifies busy poll period, idle poll period and communication timeout.
# A typical call looks like this:
# turboPmacController("$(NAME)","$(ASYN_PORT)",8,0.05,1,0.05);
# with
# 8 = Total number of axes
# 0.05 = Busy poll period in seconds
# 1 = Idle poll period in seconds
# 0.05 = Communication timeout in seconds
#
# setMaxSubsequentTimeouts:
# Set the number of subsequent timeouts which may occur before the user is informed in NICOS.
#
# setThresholdComTimeout:
# Set the maximum number of timeouts which may happen in a given timespan before the user is informed in NICOS.
# A typical call looks like this:
# setThresholdComTimeout("$(NAME)", 3600, 60);
# with
# 3600 = Timespan in seconds
# 60 = Maximum number of timeout events which may occur before the user is informed
epicsEnvSet("NAME","turboPmac1")
epicsEnvSet("ASYN_PORT","p$(NAME)")

View File

@@ -22,9 +22,6 @@ import time
from tests.conftest import check_ioc_running
from setup.classes import IocNotRunning
# Define the test folders you want to run in parallel
FOLDERS = ["tests/sinqMotor/turboPmac/ax1", "tests/sinqMotor/turboPmac/ax5"]
# Time we excpect the IOC needs to start up
TIMEOUT_IOC_STARTUP = 10
@@ -38,12 +35,22 @@ for arg in sys.argv[1:]:
else:
extra_args.append(arg)
# Find all axes paths (pa) within all controller paths (pc)
folders =[]
for pc in Path("tests").glob("*"):
if pc.is_dir() and "__pycache__" not in pc.parts:
for pa in Path(pc).glob("*"):
if pa.is_dir() and "__pycache__" not in pa.parts:
# Ignore all axis paths which start with an underscore
if not pa.parts[-1].startswith("_"):
folders.append(pa)
# Filter folders to run based on path_args
enabled_folders = []
if not path_args:
enabled_folders = FOLDERS
enabled_folders = folders
else:
for folder in FOLDERS:
for folder in folders:
if any(Path(arg).resolve().as_posix().startswith(Path(folder).resolve().as_posix()) for arg in path_args):
enabled_folders.append(folder)
@@ -72,7 +79,7 @@ processes = []
for folder in enabled_folders:
folder_path_args = (
[arg for arg in path_args if Path(arg).resolve().as_posix().startswith(Path(folder).resolve().as_posix())]
if path_args else [folder]
if path_args else [folder.as_posix()]
)
command = ["pytest"] + folder_path_args + extra_args

View File

@@ -240,11 +240,11 @@ class SinqMotor(Motor):
def error_message(self):
return self.get_pv('errormsgpv', as_string=True)
def enable_and_wait(self, timeout=10):
def enable_and_wait(self, timeout=20):
self.put_pv('enable', 1)
self.wait_enabled(timeout)
def disable_and_wait(self, timeout=10):
def disable_and_wait(self, timeout=20):
self.put_pv('enable', 0)
self.wait_disabled(timeout)
@@ -254,7 +254,7 @@ class SinqMotor(Motor):
"""
return self._wait_enabled_disabled(True, timeout)
def wait_disabled(self, timeout=10):
def wait_disabled(self, timeout=20):
"""
Wait until the motor is enabled or a timeout has been reached.
"""
@@ -276,7 +276,7 @@ class SinqMotor(Motor):
else:
pytest.fail(
f'Motor {self.pv} could not be disabled in {timeout} seconds')
time.sleep(0.1)
time.sleep(0.5)
class TurboPMAC(SinqMotor):

View File

@@ -6,4 +6,4 @@ from setup.classes import MasterMACS
@pytest.fixture(autouse=True)
def motor():
return MasterMACS('masterMacs1', 'lin1')
return MasterMACS('masterMacs1', 'ax1')

View File

@@ -1,9 +1,9 @@
# Run a selection of common tests
from tests.move import *
from tests.sinqMotor.limits import *
from tests.home import *
from tests.sinqMotor.masterMacs.reset import reset
from setup.move import *
from setup.sinqMotor.limits import *
from setup.home import *
from setup.sinqMotor.masterMacs.reset import reset
def test_reset(motor):

View File

View File

@@ -2,11 +2,11 @@
import pytest
from tests.move import *
from tests.home import *
from tests.sinqMotor.limits import *
from tests.sinqMotor.speed import *
from tests.sinqMotor.turboPmac.reset import reset
from setup.move import *
from setup.home import *
from setup.sinqMotor.limits import *
from setup.sinqMotor.speed import *
from setup.sinqMotor.turboPmac.reset import reset
def test_reset(motor):

View File

View File

@@ -1,11 +1,11 @@
# Run a selection of common tests
import pytest
from tests.move import *
from tests.sinqMotor.limits import *
from tests.sinqMotor.speed import *
from tests.home import *
from tests.sinqMotor.turboPmac.reset import reset
from setup.move import *
from setup.sinqMotor.limits import *
from setup.sinqMotor.speed import *
from setup.home import *
from setup.sinqMotor.turboPmac.reset import reset
def test_reset(motor):