Skip to content

Commit 1ea93a7

Browse files
committed
Add system test
Add a fixture to run an EPICS IOC in a subprocess. Add a test that queries the PVI PV of an IOC and verifies the structure is as expected.
1 parent 80df642 commit 1ea93a7

File tree

4 files changed

+94
-0
lines changed

4 files changed

+94
-0
lines changed

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ dev = [
4141
"sphinx-design",
4242
"tox-direct",
4343
"types-mock",
44+
"aioca",
45+
"p4p",
4446
]
4547

4648
[project.scripts]
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from p4p.client.thread import Context
2+
3+
4+
def test_ioc(ioc: None):
5+
ctxt = Context("pva")
6+
7+
parent_pvi = ctxt.get("DEVICE:PVI").todict()
8+
assert all(f in parent_pvi for f in ("alarm", "display", "timeStamp", "value"))
9+
assert parent_pvi["display"] == {"description": "The records in this controller"}
10+
assert parent_pvi["value"] == {
11+
"a": {"r": "DEVICE:A"},
12+
"b": {"r": "DEVICE:B_RBV", "w": "DEVICE:B"},
13+
"child": {"d": "DEVICE:Child:PVI"},
14+
}
15+
16+
child_pvi_pv = parent_pvi["value"]["child"]["d"]
17+
child_pvi = ctxt.get(child_pvi_pv).todict()
18+
assert all(f in child_pvi for f in ("alarm", "display", "timeStamp", "value"))
19+
assert child_pvi["display"] == {"description": "The records in this controller"}
20+
assert child_pvi["value"] == {
21+
"c": {"w": "DEVICE:Child:C"},
22+
"d": {"x": "DEVICE:Child:D"},
23+
}
24+
pass

tests/conftest.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import os
2+
import random
3+
import string
4+
import subprocess
5+
import time
6+
from pathlib import Path
27

38
import pytest
9+
from aioca import purge_channel_caches
410

511
from fastcs.attributes import AttrR, AttrRW, AttrW, Handler, Sender, Updater
612
from fastcs.controller import Controller
@@ -72,3 +78,35 @@ def controller():
7278
@pytest.fixture
7379
def mapping(controller):
7480
return Mapping(controller)
81+
82+
83+
PV_PREFIX = "".join(random.choice(string.ascii_lowercase) for _ in range(12))
84+
HERE = Path(os.path.dirname(os.path.abspath(__file__)))
85+
86+
87+
@pytest.fixture(scope="module")
88+
def ioc():
89+
process = subprocess.Popen(
90+
["python", HERE / "ioc.py"],
91+
stdin=subprocess.PIPE,
92+
stdout=subprocess.PIPE,
93+
stderr=subprocess.STDOUT,
94+
universal_newlines=True,
95+
)
96+
97+
start_time = time.monotonic()
98+
while "iocRun: All initialization complete" not in (
99+
process.stdout.readline().strip()
100+
):
101+
if time.monotonic() - start_time > 10:
102+
raise TimeoutError("IOC did not start in time")
103+
104+
yield
105+
106+
# close backend caches before the event loop
107+
purge_channel_caches()
108+
try:
109+
print(process.communicate("exit")[0])
110+
except ValueError:
111+
# Someone else already called communicate
112+
pass

tests/ioc.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from fastcs.attributes import AttrR, AttrRW, AttrW
2+
from fastcs.backends.epics.backend import EpicsBackend
3+
from fastcs.controller import Controller, SubController
4+
from fastcs.datatypes import Int
5+
from fastcs.wrappers import command
6+
7+
8+
class ParentController(Controller):
9+
a: AttrR = AttrR(Int())
10+
b: AttrRW = AttrRW(Int())
11+
12+
13+
class ChildController(SubController):
14+
c: AttrW = AttrW(Int())
15+
16+
@command()
17+
async def d(self):
18+
pass
19+
20+
21+
def run():
22+
controller = ParentController()
23+
controller.register_sub_controller("Child", ChildController())
24+
25+
backend = EpicsBackend(controller, "DEVICE")
26+
backend.run()
27+
28+
29+
if __name__ == "__main__":
30+
run()

0 commit comments

Comments
 (0)