Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

199 test helpers for assering value reading and configuration #226

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9f845db
fix strange doc test problem
Relm-Arrowny Apr 16, 2024
51ba268
added assert_value, assert_reading and assert_configuration and verif…
Relm-Arrowny Apr 17, 2024
a9f5bb3
added test for assert_value, reading and configuration both async and…
Relm-Arrowny Apr 17, 2024
d35c499
moved assert_emitted to signal together with all the helper functions
Relm-Arrowny Apr 17, 2024
a68d0d3
Merge branch 'main' into 199-test-helpers-for-assering-value-reading-…
Relm-Arrowny Apr 17, 2024
94d5016
adding docstrings
Relm-Arrowny Apr 17, 2024
c1124b6
adding docstrings
Relm-Arrowny Apr 17, 2024
c9e73aa
adding docstrings
Relm-Arrowny Apr 17, 2024
a47f96b
change Dict to Mapping and fix all the mypy issue
Relm-Arrowny Apr 17, 2024
cac9b01
fixing all the docstrings
Relm-Arrowny Apr 18, 2024
d383374
change demo to use helper function so it update the doc
Relm-Arrowny Apr 18, 2024
2e2f816
correct contamination from an other branch
Relm-Arrowny Apr 18, 2024
c02962d
Added a line and a link in how_to_test
Relm-Arrowny Apr 18, 2024
53d7e64
fixed a typo
Relm-Arrowny Apr 18, 2024
1181420
Merge branch 'main' into 199-test-helpers-for-assering-value-reading-…
Relm-Arrowny Apr 23, 2024
3f754aa
Change readable and configurable to async version.
Relm-Arrowny Apr 23, 2024
fe4c267
removed the check for async function in _verify_ready and remove tes…
Relm-Arrowny Apr 23, 2024
37ffc0c
added test_sensor_in_plan in demo.py and edited documentation to match
Relm-Arrowny Apr 23, 2024
d1afe9b
Update write-tests-for-devices.rst
Relm-Arrowny Apr 23, 2024
50c40cd
Fixed English in doc
Relm-Arrowny Apr 24, 2024
9921f85
Apply suggestions from code review
Relm-Arrowny Apr 24, 2024
5470bea
Fix not having enough ____ for write-tests-for-devices.rst
Relm-Arrowny Apr 24, 2024
c0cab3d
removed verify_reading
Relm-Arrowny Apr 24, 2024
6fdea0f
Update src/ophyd_async/core/signal.py changing DocmumentType back to …
Relm-Arrowny Apr 26, 2024
c232bf4
removed assert_emitted in test_hdf_panda.
Relm-Arrowny Apr 26, 2024
d12f783
Merge branch 'main' into 199-test-helpers-for-assering-value-reading-…
Relm-Arrowny Apr 26, 2024
261427d
fix deprecation aftering resolving conflict with main
Relm-Arrowny Apr 26, 2024
5e1cdc3
fix auto correct
Relm-Arrowny Apr 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/ophyd_async/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
SignalRW,
SignalW,
SignalX,
assert_configuration,
assert_emitted,
assert_reading,
assert_value,
observe_value,
set_and_wait_for_value,
set_sim_callback,
Expand Down Expand Up @@ -99,4 +103,8 @@
"walk_rw_signals",
"load_device",
"save_device",
"assert_reading",
"assert_value",
"assert_configuration",
"assert_emitted",
]
66 changes: 58 additions & 8 deletions src/ophyd_async/core/signal.py
coretl marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@

import asyncio
import functools
from typing import AsyncGenerator, Callable, Dict, Generic, Optional, Union
from typing import (
Any,
AsyncGenerator,
Awaitable,
Callable,
Dict,
Generic,
Optional,
Union,
)

from bluesky.protocols import (
Configurable,
coretl marked this conversation as resolved.
Show resolved Hide resolved
Descriptor,
Locatable,
Location,
Expand Down Expand Up @@ -48,16 +58,10 @@ def __init__(
self, backend: SignalBackend[T], timeout: Optional[float] = DEFAULT_TIMEOUT
) -> None:
self._name = ""
self._long_name = None
coretl marked this conversation as resolved.
Show resolved Hide resolved
self._timeout = timeout
self._init_backend = self._backend = backend

@property
def name(self) -> str:
return self._name

def set_name(self, name: str = ""):
self._name = name

async def connect(self, sim=False, timeout=DEFAULT_TIMEOUT):
if sim:
self._backend = SimSignalBackend(
Expand Down Expand Up @@ -253,6 +257,52 @@ def set_sim_callback(signal: Signal[T], callback: ReadingValueCallback[T]) -> No
return _sim_backends[signal].set_callback(callback)


async def verify_readable(
coretl marked this conversation as resolved.
Show resolved Hide resolved
func: Callable[[], dict[str, Any] | Awaitable[dict[str, Any]]],
expectation: Dict[str, Any],
) -> None:
"""Loop thorough dictionary and verify it against
a expected dictionary (readable)"""
if asyncio.iscoroutinefunction(func):
result = await func()
else:
result = func()
coretl marked this conversation as resolved.
Show resolved Hide resolved

for signal in expectation:
for field in expectation[signal]:
if field == "timestamp":
assert isinstance(result[signal]["timestamp"], float)
else:
assert result[signal][field] == expectation[signal][field]
coretl marked this conversation as resolved.
Show resolved Hide resolved


async def assert_value(signal: SignalR[T], value: T) -> None:
"""Assert a signal value"""
assert await signal.get_value() == value


async def assert_reading(
readable: Readable, reading: Dict[str, Reading] | dict[str, dict[str, Any]]
coretl marked this conversation as resolved.
Show resolved Hide resolved
) -> None:
"""Assert readable by calling verify_readable"""
await verify_readable(readable.read, reading)


async def assert_configuration(
configurable: Configurable,
coretl marked this conversation as resolved.
Show resolved Hide resolved
configuration: Dict[str, Reading] | dict[str, dict[str, Any]],
) -> None:
"""Assert readable generated by configurable.read_configuration
using verify_readable"""
await verify_readable(configurable.read_configuration, configuration)


def assert_emitted(docs: Dict[str, list], **numbers: int):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can import DocumentType from event model

Suggested change
def assert_emitted(docs: Dict[str, list], **numbers: int):
def assert_emitted(docs: DocumentType, **numbers: int):

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although docs is a dict of document name to list of document instances, so I think that should be:

Suggested change
def assert_emitted(docs: Dict[str, list], **numbers: int):
def assert_emitted(docs: Dict[str, list[DocumentType]], **numbers: int):

Except DocumentType is actually the types, which is not what we want, we would quite like a similar Document union in event model...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so in the absence of that it's probably as correct as it could be...

"""Assert emitted"""
assert list(docs) == list(numbers)
assert {name: len(d) for name, d in docs.items()} == numbers


async def observe_value(signal: SignalR[T], timeout=None) -> AsyncGenerator[T, None]:
"""Subscribe to the value of a signal so it can be iterated from.

Expand Down
81 changes: 81 additions & 0 deletions tests/core/test_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@
import pytest

from ophyd_async.core import (
DeviceCollector,
Signal,
SignalRW,
SimSignalBackend,
StandardReadable,
assert_configuration,
assert_reading,
assert_value,
set_and_wait_for_value,
set_sim_put_proceeds,
set_sim_value,
wait_for_value,
)
from ophyd_async.core.utils import DEFAULT_TIMEOUT
from ophyd_async.epics.signal import epics_signal_r, epics_signal_rw


class MySignal(Signal):
Expand Down Expand Up @@ -119,3 +125,78 @@ async def test_set_and_wait_for_value():
assert not st.done
set_sim_put_proceeds(sim_signal, True)
assert await time_taken_by(st) < 0.1


@pytest.fixture
async def sim_signal():
sim_signal = SignalRW(SimSignalBackend(int, "test"))
sim_signal.set_name("sim_signal")
await sim_signal.connect(sim=True)
yield sim_signal


async def test_assert_value(sim_signal: SignalRW):
set_sim_value(sim_signal, 168)
await assert_value(sim_signal, 168)


async def test_assert_reaading(sim_signal: SignalRW):
set_sim_value(sim_signal, 888)
dummy_reading = {
"sim_signal": {"alarm_severity": 0, "timestamp": 46709394.28, "value": 888}
}
await assert_reading(sim_signal, dummy_reading)


class DummyReadable(StandardReadable):
"""A demo sensor that produces a scalar value based on X and Y Movers"""

def __init__(self, prefix: str, name="") -> None:
# Define some signals
self.value = epics_signal_r(float, prefix + "Value")
self.mode = epics_signal_rw(str, prefix + "Mode")
self.mode2 = epics_signal_rw(str, prefix + "Mode2")
# Set name and signals for read() and read_configuration()
self.set_readable_signals(
read=[self.value],
config=[self.mode, self.mode2],
)
super().__init__(name=name)


@pytest.fixture
async def sim_readable():
async with DeviceCollector(sim=True):
sim_readable = DummyReadable("SIM:READABLE:")
# Signals connected here
assert sim_readable.name == "sim_readable"
yield sim_readable


async def test_assert_configuration(sim_readable: DummyReadable):
set_sim_value(sim_readable.value, 123)
set_sim_value(sim_readable.mode, "super mode")
set_sim_value(sim_readable.mode2, "slow mode")
dummy_config_reading = {
"sim_readable-mode": {
"alarm_severity": 0,
"timestamp": 123.2,
"value": "super mode",
},
"sim_readable-mode2": {
"alarm_severity": 0,
"timestamp": 123.2,
"value": "slow mode",
},
}
await assert_configuration(sim_readable, dummy_config_reading)
# test for none awaitable part of verify
from ophyd.sim import DetWithConf

something = DetWithConf(name="det")
dummy_config_reading1 = {
"det_c": {"value": 3, "timestamp": 171},
"det_d": {"value": 4, "timestamp": 1753},
}

await assert_configuration(something, dummy_config_reading1)
7 changes: 1 addition & 6 deletions tests/sim/test_streaming_plan.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
from collections import defaultdict
from typing import Dict

from bluesky import plans as bp
from bluesky.run_engine import RunEngine

from ophyd_async.core.signal import assert_emitted
from ophyd_async.sim.sim_pattern_generator import SimPatternDetector


def assert_emitted(docs: Dict[str, list], **numbers: int):
assert list(docs) == list(numbers)
assert {name: len(d) for name, d in docs.items()} == numbers


# NOTE the async operations with h5py are non-trival
# because of lack of native support for async operations
# see https://github.com/h5py/h5py/issues/837
Expand Down
Loading