-
Notifications
You must be signed in to change notification settings - Fork 10
/
conftest.py
161 lines (128 loc) · 4.73 KB
/
conftest.py
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import asyncio
import logging
import os
import sys
import time
from collections.abc import Mapping
from os import environ, getenv
from pathlib import Path
from typing import Any
from unittest.mock import MagicMock, patch
import pytest
from bluesky.run_engine import RunEngine
from ophyd.status import Status
from ophyd_async.core import (
PathInfo,
PathProvider,
)
from dodal.common.beamlines import beamline_utils
from dodal.common.visit import (
DirectoryServiceClient,
LocalDirectoryServiceClient,
StaticVisitPathProvider,
)
from dodal.log import LOGGER, GELFTCPHandler, set_up_all_logging_handlers
MOCK_DAQ_CONFIG_PATH = "tests/devices/unit_tests/test_daq_configuration"
mock_paths = [
("DAQ_CONFIGURATION_PATH", MOCK_DAQ_CONFIG_PATH),
("ZOOM_PARAMS_FILE", "tests/devices/unit_tests/test_jCameraManZoomLevels.xml"),
("DISPLAY_CONFIG", "tests/devices/unit_tests/test_display.configuration"),
("LOOK_UPTABLE_DIR", "tests/devices/i10/lookupTables/"),
]
mock_attributes_table = {
"i03": mock_paths,
"i10": mock_paths,
"s03": mock_paths,
"i04": mock_paths,
"s04": mock_paths,
"i24": mock_paths,
}
BANNED_PATHS = [Path("/dls"), Path("/dls_sw")]
environ["DODAL_TEST_MODE"] = "true"
@pytest.fixture(autouse=True)
def patch_open_to_prevent_dls_reads_in_tests():
unpatched_open = open
def patched_open(*args, **kwargs):
requested_path = Path(args[0])
if requested_path.is_absolute():
for p in BANNED_PATHS:
assert not requested_path.is_relative_to(
p
), f"Attempt to open {requested_path} from inside a unit test"
return unpatched_open(*args, **kwargs)
with patch("builtins.open", side_effect=patched_open):
yield []
# Prevent pytest from catching exceptions when debugging in vscode so that break on
# exception works correctly (see: https://github.com/pytest-dev/pytest/issues/7409)
if os.getenv("PYTEST_RAISE", "0") == "1":
@pytest.hookimpl(tryfirst=True)
def pytest_exception_interact(call: pytest.CallInfo[Any]):
if call.excinfo is not None:
raise call.excinfo.value
else:
raise RuntimeError(
f"{call} has no exception data, an unknown error has occurred"
)
@pytest.hookimpl(tryfirst=True)
def pytest_internalerror(excinfo: pytest.ExceptionInfo[Any]):
raise excinfo.value
def pytest_runtest_setup(item):
beamline_utils.clear_devices()
if LOGGER.handlers == []:
mock_graylog_handler = MagicMock(spec=GELFTCPHandler)
mock_graylog_handler.return_value.level = logging.DEBUG
with patch("dodal.log.GELFTCPHandler", mock_graylog_handler):
set_up_all_logging_handlers(
LOGGER, Path("./tmp/dev"), "dodal.log", True, 10000
)
def pytest_runtest_teardown():
if "dodal.beamlines.beamline_utils" in sys.modules:
sys.modules["dodal.beamlines.beamline_utils"].clear_devices()
s03_epics_server_port = getenv("S03_EPICS_CA_SERVER_PORT")
s03_epics_repeater_port = getenv("S03_EPICS_CA_REPEATER_PORT")
if s03_epics_server_port is not None:
environ["EPICS_CA_SERVER_PORT"] = s03_epics_server_port
print(f"[EPICS_CA_SERVER_PORT] = {s03_epics_server_port}")
if s03_epics_repeater_port is not None:
environ["EPICS_CA_REPEATER_PORT"] = s03_epics_repeater_port
print(f"[EPICS_CA_REPEATER_PORT] = {s03_epics_repeater_port}")
PATH_INFO_FOR_TESTING: PathInfo = PathInfo(
directory_path=Path("/does/not/exist"),
filename="on_this_filesystem",
)
@pytest.fixture
def dummy_visit_client() -> DirectoryServiceClient:
return LocalDirectoryServiceClient()
@pytest.fixture
async def static_path_provider(
tmp_path: Path, dummy_visit_client: DirectoryServiceClient
) -> PathProvider:
svpp = StaticVisitPathProvider(
beamline="ixx", root=tmp_path, client=dummy_visit_client
)
await svpp.update()
return svpp
@pytest.fixture
def run_engine_documents(RE: RunEngine) -> Mapping[str, list[dict]]:
docs: dict[str, list[dict]] = {}
def append_and_print(name, doc):
if name not in docs:
docs[name] = []
docs[name] += [doc]
RE.subscribe(append_and_print)
return docs
def failed_status(failure: Exception) -> Status:
status = Status()
status.set_exception(failure)
return status
@pytest.fixture
async def RE():
RE = RunEngine()
# make sure the event loop is thoroughly up and running before we try to create
# any ophyd_async devices which might need it
timeout = time.monotonic() + 1
while not RE.loop.is_running():
await asyncio.sleep(0)
if time.monotonic() > timeout:
raise TimeoutError("This really shouldn't happen but just in case...")
yield RE