Skip to content

Commit aaa003d

Browse files
committed
Remaining test fixes for async conversion
Awaited on functions where required. I also had to switch to AsyncMock from MagicMock anywhere that a function needed to return a coroutine.
1 parent 34a76f8 commit aaa003d

File tree

9 files changed

+119
-96
lines changed

9 files changed

+119
-96
lines changed

tests/conftest.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import asyncio
2-
import sys
3-
import types
41
from unittest import mock
52

63
import pytest
@@ -28,15 +25,21 @@
2825
def x_device():
2926
x_device = mock.MagicMock()
3027
x_device.name = "x_device"
28+
x_device.get_value = mock.AsyncMock()
3129
x_device.get_value.return_value = DUMMY_VALUE_1
30+
x_device.set_value = mock.AsyncMock()
31+
3232
return x_device
3333

3434

3535
@pytest.fixture
3636
def y_device():
3737
y_device = mock.MagicMock()
3838
y_device.name = "y_device"
39+
y_device.get_value = mock.AsyncMock()
3940
y_device.get_pv_name.return_value = SP_PV
41+
y_device.set_value = mock.AsyncMock()
42+
4043
return y_device
4144

4245

@@ -45,7 +48,11 @@ def y_device():
4548
def mock_sim_data_source():
4649
mock_sim_data_source = mock.MagicMock()
4750
mock_sim_data_source.units = pytac.PHYS
51+
52+
mock_sim_data_source.get_value = mock.AsyncMock()
4853
mock_sim_data_source.get_value.return_value = DUMMY_VALUE_2
54+
mock_sim_data_source.set_value = mock.AsyncMock()
55+
4956
return mock_sim_data_source
5057

5158

@@ -108,8 +115,7 @@ async def diad_ring():
108115

109116
@pytest.fixture
110117
async def lattice():
111-
lat = await load_csv.load("dummy", mock.MagicMock(), CURRENT_DIR_PATH / "data", 2)
112-
return lat
118+
return await load_csv.load("dummy", mock.MagicMock(), CURRENT_DIR_PATH / "data", 2)
113119

114120

115121
def set_func(pvs, values, throw=None):
@@ -120,8 +126,11 @@ def set_func(pvs, values, throw=None):
120126
@pytest.fixture
121127
def mock_cs():
122128
cs = mock.MagicMock()
129+
cs.get_single = mock.AsyncMock()
123130
cs.get_single.return_value = DUMMY_VALUE_1
131+
cs.get_multiple = mock.AsyncMock()
124132
cs.get_multiple.return_value = DUMMY_ARRAY
133+
cs.set_multiple = mock.AsyncMock()
125134
cs.set_multiple.side_effect = set_func
126135
return cs
127136

tests/test_aioca_cs.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,37 @@
1-
"""Tests for the AIOCAControlSystem class.
2-
"""
1+
"""Tests for the AIOCAControlSystem class."""
2+
3+
from unittest.mock import MagicMock, patch
34

4-
from unittest.mock import patch, MagicMock
55
import pytest
66
from constants import RB_PV, SP_PV
77
from testfixtures import LogCapture
88

99
import pytac
1010
from pytac.aioca_cs import AIOCAControlSystem
1111

12+
1213
class CANothing(Exception):
1314
"""A minimal mock of the cothread CANothing exception class."""
1415

1516
def __init__(self, name, errorcode=True):
1617
self.ok = errorcode
1718
self.name = name
1819

20+
1921
@pytest.fixture
2022
def cs():
2123
return AIOCAControlSystem(wait=True, timeout=2.0)
2224

2325

2426
@patch("pytac.aioca_cs.caget")
25-
async def test_get_single_calls_caget_correctly(caget:MagicMock, cs):
27+
async def test_get_single_calls_caget_correctly(caget: MagicMock, cs):
2628
caget.return_value = 42
2729
assert (await cs.get_single(RB_PV)) == 42
2830
caget.assert_called_with(RB_PV, throw=True, timeout=2.0)
2931

3032

3133
@patch("pytac.aioca_cs.caget")
32-
async def test_get_multiple_calls_caget_correctly(caget:MagicMock, cs):
34+
async def test_get_multiple_calls_caget_correctly(caget: MagicMock, cs):
3335
"""caget is called with throw=False despite throw=True being the default
3436
for get_multiple as we always want our get operation to fully complete,
3537
rather than being stopped halway through by an error raised from
@@ -42,13 +44,13 @@ async def test_get_multiple_calls_caget_correctly(caget:MagicMock, cs):
4244

4345

4446
@patch("pytac.aioca_cs.caput")
45-
async def test_set_single_calls_caput_correctly(caput:MagicMock, cs):
47+
async def test_set_single_calls_caput_correctly(caput: MagicMock, cs):
4648
assert await cs.set_single(SP_PV, 42) is True
4749
caput.assert_called_with(SP_PV, 42, throw=True, timeout=2.0, wait=True)
4850

4951

5052
@patch("pytac.aioca_cs.caput")
51-
async def test_set_multiple_calls_caput_correctly(caput:MagicMock, cs):
53+
async def test_set_multiple_calls_caput_correctly(caput: MagicMock, cs):
5254
"""caput is called with throw=False despite throw=True being the default
5355
for set_multiple as we always want our set operation to fully complete,
5456
rather than being stopped halway through by an error raised from
@@ -63,7 +65,7 @@ async def test_set_multiple_calls_caput_correctly(caput:MagicMock, cs):
6365

6466
@patch("pytac.aioca_cs.caget")
6567
@patch("pytac.aioca_cs.CANothing", CANothing)
66-
async def test_get_multiple_raises_ControlSystemException(caget:MagicMock, cs):
68+
async def test_get_multiple_raises_ControlSystemException(caget: MagicMock, cs):
6769
"""Here we check that errors are thrown, suppressed and logged correctly."""
6870
caget.return_value = [12, CANothing("pv", False)]
6971
with pytest.raises(pytac.exceptions.ControlSystemException):
@@ -75,19 +77,22 @@ async def test_get_multiple_raises_ControlSystemException(caget:MagicMock, cs):
7577

7678
@patch("pytac.aioca_cs.caput")
7779
@patch("pytac.aioca_cs.CANothing", CANothing)
78-
async def test_set_multiple_raises_ControlSystemException(caput:MagicMock, cs):
80+
async def test_set_multiple_raises_ControlSystemException(caput: MagicMock, cs):
7981
"""Here we check that errors are thrown, suppressed and logged correctly."""
8082
caput.return_value = [CANothing("pv1", True), CANothing("pv2", False)]
8183
with pytest.raises(pytac.exceptions.ControlSystemException):
8284
await cs.set_multiple([RB_PV, SP_PV], [42, 6])
8385
with LogCapture() as log:
84-
assert await cs.set_multiple([RB_PV, SP_PV], [42, 6], throw=False) == [True, False]
86+
assert await cs.set_multiple([RB_PV, SP_PV], [42, 6], throw=False) == [
87+
True,
88+
False,
89+
]
8590
log.check(("root", "WARNING", "Cannot connect to pv2."))
8691

8792

8893
@patch("pytac.aioca_cs.caget")
8994
@patch("pytac.aioca_cs.CANothing", CANothing)
90-
async def test_get_single_raises_ControlSystemException(caget:MagicMock, cs):
95+
async def test_get_single_raises_ControlSystemException(caget: MagicMock, cs):
9196
"""Here we check that errors are thrown, suppressed and logged correctly."""
9297
caget.side_effect = CANothing("pv", False)
9398
with LogCapture() as log:
@@ -99,7 +104,7 @@ async def test_get_single_raises_ControlSystemException(caget:MagicMock, cs):
99104

100105
@patch("pytac.aioca_cs.caput")
101106
@patch("pytac.aioca_cs.CANothing", CANothing)
102-
async def test_set_single_raises_ControlSystemException(caput:MagicMock, cs):
107+
async def test_set_single_raises_ControlSystemException(caput: MagicMock, cs):
103108
"""Here we check that errors are thrown, suppressed and logged correctly."""
104109
caput.side_effect = CANothing("pv", False)
105110
with LogCapture() as log:

tests/test_data_source.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ def test_get_fields(simple_object, request):
3232
@pytest.mark.parametrize(
3333
"simple_object", ["simple_element", "simple_lattice", "simple_data_source_manager"]
3434
)
35-
def test_set_value(simple_object, request):
35+
async def test_set_value(simple_object, request):
3636
simple_object = request.getfixturevalue(simple_object)
37-
simple_object.set_value("x", DUMMY_VALUE_2, pytac.ENG, pytac.LIVE)
37+
await simple_object.set_value("x", DUMMY_VALUE_2, pytac.ENG, pytac.LIVE)
3838
simple_object.get_device("x").set_value.assert_called_with(DUMMY_VALUE_2, True)
3939

4040

@@ -44,14 +44,15 @@ def test_set_value(simple_object, request):
4444
async def test_get_value_sim(simple_object, request):
4545
simple_object = request.getfixturevalue(simple_object)
4646
assert (
47-
await simple_object.get_value("x", pytac.RB, pytac.PHYS, pytac.SIM) == DUMMY_VALUE_2
47+
await simple_object.get_value("x", pytac.RB, pytac.PHYS, pytac.SIM)
48+
== DUMMY_VALUE_2
4849
)
4950

5051

5152
@pytest.mark.parametrize(
5253
"simple_object", ["simple_element", "simple_lattice", "simple_data_source_manager"]
5354
)
54-
def test_unit_conversion(simple_object, double_uc, request):
55+
async def test_unit_conversion(simple_object, double_uc, request):
5556
simple_object = request.getfixturevalue(simple_object)
56-
simple_object.set_value("y", DUMMY_VALUE_2, pytac.PHYS, pytac.LIVE)
57+
await simple_object.set_value("y", DUMMY_VALUE_2, pytac.PHYS, pytac.LIVE)
5758
simple_object.get_device("y").set_value.assert_called_with(DUMMY_VALUE_2 / 2, True)

tests/test_device.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
def create_epics_device(prefix=PREFIX, rb_pv=RB_PV, sp_pv=SP_PV, enabled=True):
1212
mock_cs = mock.MagicMock()
13+
mock_cs.set_single = mock.AsyncMock()
14+
mock_cs.get_single = mock.AsyncMock()
1315
mock_cs.get_single.return_value = 40.0
1416
device = EpicsDevice(prefix, mock_cs, enabled=enabled, rb_pv=rb_pv, sp_pv=sp_pv)
1517
return device
@@ -21,27 +23,27 @@ def create_simple_device(value=1.0, enabled=True):
2123

2224

2325
# Epics device specific tests.
24-
def test_set_epics_device_value():
26+
async def test_set_epics_device_value():
2527
device = create_epics_device()
26-
device.set_value(40)
28+
await device.set_value(40)
2729
device._cs.set_single.assert_called_with(SP_PV, 40, True)
2830

2931

30-
def test_get_epics_device_value():
32+
async def test_get_epics_device_value():
3133
device = create_epics_device()
32-
assert device.get_value(pytac.SP) == 40.0
34+
assert await device.get_value(pytac.SP) == 40.0
3335

3436

35-
def test_epics_device_invalid_sp_raises_exception():
37+
async def test_epics_device_invalid_sp_raises_exception():
3638
device2 = create_epics_device(PREFIX, RB_PV, None)
3739
with pytest.raises(pytac.exceptions.HandleException):
38-
device2.set_value(40)
40+
await device2.set_value(40)
3941

4042

41-
def test_get_epics_device_value_invalid_handle_raises_exception():
43+
async def test_get_epics_device_value_invalid_handle_raises_exception():
4244
device = create_epics_device()
4345
with pytest.raises(pytac.exceptions.HandleException):
44-
device.get_value("non_existent")
46+
await device.get_value("non_existent")
4547

4648

4749
# Simple device specific tests.
@@ -93,8 +95,8 @@ def test_device_is_enabled_returns_bool_value(device_creation_function):
9395

9496

9597
# PvEnabler test.
96-
def test_PvEnabler(mock_cs):
98+
async def test_PvEnabler(mock_cs):
9799
pve = PvEnabler("enable-pv", 40, mock_cs)
98-
assert pve
100+
assert await pve.is_enabled()
99101
mock_cs.get_single.return_value = 50
100-
assert not pve
102+
assert not await pve.is_enabled()

tests/test_element.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -96,49 +96,51 @@ async def test_get_value_uses_uc_if_necessary_for_cs_call(simple_element, double
9696
) == (DUMMY_VALUE_1 * 2)
9797

9898

99-
def test_get_value_uses_uc_if_necessary_for_sim_call(simple_element, double_uc):
99+
async def test_get_value_uses_uc_if_necessary_for_sim_call(simple_element, double_uc):
100100
simple_element._data_source_manager._uc["x"] = double_uc
101-
assert simple_element.get_value(
101+
assert await simple_element.get_value(
102102
"x", handle=pytac.SP, units=pytac.ENG, data_source=pytac.SIM
103103
) == (DUMMY_VALUE_2 / 2)
104104
simple_element._data_source_manager._data_sources[
105105
pytac.SIM
106106
].get_value.assert_called_with("x", pytac.SP, True)
107107

108108

109-
def test_set_value_eng(simple_element):
110-
simple_element.set_value("x", DUMMY_VALUE_2)
109+
async def test_set_value_eng(simple_element):
110+
await simple_element.set_value("x", DUMMY_VALUE_2)
111111
# No conversion needed
112112
simple_element.get_device("x").set_value.assert_called_with(DUMMY_VALUE_2, True)
113113

114114

115-
def test_set_value_phys(simple_element, double_uc):
115+
async def test_set_value_phys(simple_element, double_uc):
116116
simple_element._data_source_manager._uc["x"] = double_uc
117-
simple_element.set_value("x", DUMMY_VALUE_2, units=pytac.PHYS)
117+
await simple_element.set_value("x", DUMMY_VALUE_2, units=pytac.PHYS)
118118
# Conversion fron physics to engineering units
119119
simple_element.get_device("x").set_value.assert_called_with(DUMMY_VALUE_2 / 2, True)
120120

121121

122-
def test_set_exceptions(simple_element, unit_uc):
122+
async def test_set_exceptions(simple_element, unit_uc):
123123
with pytest.raises(pytac.exceptions.FieldException):
124-
simple_element.set_value("unknown_field", 40.0)
124+
await simple_element.set_value("unknown_field", 40.0)
125125
with pytest.raises(pytac.exceptions.DataSourceException):
126-
simple_element.set_value("y", 40.0, data_source="unknown_data_source")
126+
await simple_element.set_value("y", 40.0, data_source="unknown_data_source")
127127
simple_element._data_source_manager._uc["uc_but_no_data_source"] = unit_uc
128128
with pytest.raises(pytac.exceptions.FieldException):
129-
simple_element.set_value("uc_but_no_data_source", 40.0)
129+
await simple_element.set_value("uc_but_no_data_source", 40.0)
130130

131131

132-
def test_get_exceptions(simple_element):
132+
async def test_get_exceptions(simple_element):
133133
with pytest.raises(pytac.exceptions.FieldException):
134-
simple_element.get_value("unknown_field", "setpoint")
134+
await simple_element.get_value("unknown_field", "setpoint")
135135
with pytest.raises(pytac.exceptions.DataSourceException):
136-
simple_element.get_value("y", "setpoint", data_source="unknown_data_source")
136+
await simple_element.get_value(
137+
"y", "setpoint", data_source="unknown_data_source"
138+
)
137139

138140

139-
def test_identity_conversion(simple_element):
140-
value_physics = simple_element.get_value("x", "setpoint", pytac.PHYS)
141-
value_machine = simple_element.get_value("x", "setpoint", pytac.ENG)
141+
async def test_identity_conversion(simple_element):
142+
value_physics = await simple_element.get_value("x", "setpoint", pytac.PHYS)
143+
value_machine = await simple_element.get_value("x", "setpoint", pytac.ENG)
142144
assert value_machine == DUMMY_VALUE_1
143145
assert value_physics == DUMMY_VALUE_1
144146

0 commit comments

Comments
 (0)