-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathconftest.py
More file actions
140 lines (108 loc) · 4.13 KB
/
conftest.py
File metadata and controls
140 lines (108 loc) · 4.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import atexit
import os
import random
import string
import subprocess
import sys
from datetime import datetime
import pytest
from epicsdbbuilder import ResetRecords
# Must import softioc before epicsdbbuilder
from softioc.device_core import RecordLookup
requires_cothread = pytest.mark.skipif(
sys.platform.startswith("win"), reason="Cothread doesn't work on windows"
)
# Default length used to initialise Waveform and longString records.
# Length picked to match string record length, so we can re-use test strings.
WAVEFORM_LENGTH = 40
# Default timeout for many operations across testing
TIMEOUT = 10 # Seconds
# Address for multiprocessing Listener/Client pair
ADDRESS = ("localhost", 2345)
def create_random_prefix():
"""Create 12-character random string, for generating unique Device Names"""
return "".join(random.choice(string.ascii_uppercase) for _ in range(12))
# Can't use logging as it's not multiprocess safe, and
# alteratives are overkill
def log(*args):
print(datetime.now().strftime("%H:%M:%S"), *args)
class SubprocessIOC:
def __init__(self, ioc_py):
self.pv_prefix = create_random_prefix()
sim_ioc = os.path.join(os.path.dirname(__file__), ioc_py)
cmd = [sys.executable, sim_ioc, self.pv_prefix]
self.proc = subprocess.Popen(
cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
def kill(self):
if self.proc.returncode is None:
# still running, kill it and print the output
self.proc.kill()
out, err = self.proc.communicate(timeout=TIMEOUT)
print(out.decode())
print(err.decode())
def aioca_cleanup():
from aioca import _catools, purge_channel_caches
# Unregister the aioca atexit handler as it conflicts with the one installed
# by cothread. If we don't do this we get a seg fault. This is not a problem
# in production as we won't mix aioca and cothread, but we do mix them in
# the tests so need to do this.
atexit.unregister(_catools._catools_atexit)
# purge the channels before the event loop goes
purge_channel_caches()
@pytest.fixture
def pfc_ioc():
ioc = SubprocessIOC("test_pfc_wrapper.py")
yield ioc
ioc.kill()
aioca_cleanup()
# @pytest.fixture
# def asyncio_ioc_override():
# ioc = SubprocessIOC("sim_asyncio_ioc_override.py")
# yield ioc
# ioc.kill()
# aioca_cleanup()
def _clear_records():
# Remove any records created at epicsdbbuilder layer
ResetRecords()
# And at pythonSoftIoc level
# TODO: Remove this hack and use use whatever comes out of
# https://github.com/dls-controls/pythonSoftIOC/issues/56
RecordLookup._RecordDirectory.clear()
@pytest.fixture(autouse=True)
def clear_records():
"""Deletes all records before and after every test"""
_clear_records()
yield
_clear_records()
@pytest.fixture(autouse=True)
def enable_code_coverage():
"""Ensure code coverage works as expected for `multiprocesses` tests.
As its harmless for other types of test, we always run this fixture."""
try:
from pytest_cov.embed import cleanup_on_sigterm
except ImportError:
pass
else:
cleanup_on_sigterm()
def select_and_recv(conn, expected_char=None):
"""Wait for the given Connection to have data to receive, and return it.
If a character is provided check its correct before returning it."""
# Must use cothread's select if cothread is present, otherwise we'd block
# processing on all cothread processing. But we don't want to use it
# unless we have to, as importing cothread can cause issues with forking.
if "cothread" in sys.modules:
from cothread import select
rrdy, _, _ = select([conn], [], [], TIMEOUT)
else:
# Would use select.select(), but Windows doesn't accept Pipe handles
# as selectable objects.
if conn.poll(TIMEOUT):
rrdy = True
if rrdy:
val = conn.recv()
else:
pytest.fail("Did not receive expected char before TIMEOUT expired")
if expected_char:
assert val == expected_char, "Expected character did not match"
return val