Skip to content

Commit

Permalink
More strict nexus structure validation and tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
YooSunYoung committed Sep 26, 2024
1 parent 5e9ceea commit a893cec
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 48 deletions.
56 changes: 48 additions & 8 deletions src/beamlime/applications/_nexus_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,48 @@ def _validate_module_keys(
)


def _validate_f144_module_spec(
module_spec: StreamModuleValue,
) -> None:
"""Validate the f144 module."""
if len(module_spec.parent["children"]) != 1:
raise InvalidNexusStructureError(
"Group containing f144 module should have exactly one child"
)
if module_spec.dtype is None or module_spec.value_units is None:
raise InvalidNexusStructureError(
"f144 module spec should have dtype and value_units"
)


def _validate_f144_module_values(
key_value_dict: dict[StreamModuleKey, StreamModuleValue],
) -> None:
"""Validate the module values for the f144 module."""
for key, value in key_value_dict.items():
if key.module_type == "f144":
_validate_f144_module_spec(value)


def _validate_ev44_module_spec(
module_spec: StreamModuleValue,
) -> None:
"""Validate the ev44 module."""
if len(module_spec.parent["children"]) != 1:
raise InvalidNexusStructureError(
"Group containing ev44 module should have exactly one child"
)


def _validate_ev44_module_values(
key_value_dict: dict[StreamModuleKey, StreamModuleValue],
) -> None:
"""Validate the module values for the ev44 module."""
for key, value in key_value_dict.items():
if key.module_type == "ev44":
_validate_ev44_module_spec(value)


def collect_streaming_modules(
structure: Mapping,
) -> dict[StreamModuleKey, StreamModuleValue]:
Expand Down Expand Up @@ -222,7 +264,10 @@ def collect_streaming_modules(
)
_validate_module_configs(key_value_pairs)
_validate_module_keys(key_value_pairs)
return dict(key_value_pairs)
key_value_dict = dict(key_value_pairs)
_validate_f144_module_values(key_value_dict)
_validate_ev44_module_values(key_value_dict)
return key_value_dict


def create_dataset(
Expand Down Expand Up @@ -258,8 +303,7 @@ def _is_monitor(group: NexusGroup) -> bool:

def _initialize_ev44(module_spec: StreamModuleValue) -> NexusGroup:
parent = module_spec.parent
if len(parent['children']) != 1:
raise ValueError('Group containing ev44 module should have exactly one child')
_validate_ev44_module_spec(module_spec)
group: NexusGroup = cast(NexusGroup, parent.copy())
group['children'] = [
create_dataset(
Expand Down Expand Up @@ -346,11 +390,7 @@ def _merge_ev44(group: NexusGroup, data: DeserializedMessage) -> None:

def _initialize_f144(module_spec: StreamModuleValue) -> NexusGroup:
parent = module_spec.parent
if len(parent['children']) != 1:
raise ValueError('Group containing f144 module should have exactly one child')
if module_spec.dtype is None:
raise ValueError('f144 module spec should have dtype')

_validate_f144_module_spec(module_spec)
group: NexusGroup = cast(NexusGroup, parent.copy())
group["children"] = [
create_dataset(
Expand Down
153 changes: 123 additions & 30 deletions tests/applications/nexus_helpers_test.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
import hashlib
import json
import pathlib
from collections.abc import Generator, Mapping

import numpy as np
import pytest

from beamlime.applications._nexus_helpers import (
InvalidNexusStructureError,
StreamModuleKey,
StreamModuleValue,
collect_streaming_modules,
Expand Down Expand Up @@ -112,21 +112,6 @@ def test_find_nexus_structure_not_found_raises() -> None:
find_nexus_structure({}, ("b0",))


def test_invalid_nexus_template_multiple_module_placeholders() -> None:
with open(pathlib.Path(__file__).parent / "multiple_modules_datagroup.json") as f:
modules = collect_streaming_modules(json.load(f))

key = StreamModuleKey("ev44", "hypothetical_detector", "ymir_00")
spec = modules[key]
with pytest.raises(ValueError, match="should have exactly one child"):
merge_message_into_nexus_store(
module_key=key,
module_spec=spec,
nexus_store={},
data={}, # data does not matter since it reaches the error first.
)


def test_ymir_detector_template_checksum() -> None:
"""Test that the ymir template with detectors is updated.
Expand Down Expand Up @@ -308,6 +293,46 @@ def test_nxevent_data_ev44_generator_yields_frame_by_frame() -> None:
next(ev44)


def test_ev44_merge_no_children_raises() -> None:
key = StreamModuleKey("ev44", "", "")
wrong_value = StreamModuleValue(
path=("",),
parent={"children": []},
dtype="int32",
value_units="km",
)
with pytest.raises(
InvalidNexusStructureError,
match="Group containing ev44 module should have exactly one child",
):
merge_message_into_nexus_store(
module_key=key,
module_spec=wrong_value,
nexus_store={},
data={},
)


def test_ev44_merge_too_many_children_raises() -> None:
key = StreamModuleKey("ev44", "", "")
wrong_value = StreamModuleValue(
path=("",),
parent={"children": []},
dtype="int32",
value_units="km",
)
with pytest.raises(
InvalidNexusStructureError,
match="Group containing ev44 module should have exactly one child",
):
merge_message_into_nexus_store(
module_key=key,
module_spec=wrong_value,
nexus_store={},
data={},
)


@pytest.fixture()
def nexus_template_with_streamed_log(dtype):
return {
Expand Down Expand Up @@ -343,7 +368,7 @@ def f144_event_generator(shape, dtype):

@pytest.mark.parametrize('shape', [(1,), (2,), (2, 2)])
@pytest.mark.parametrize('dtype', ['int32', 'uint32', 'float32', 'float64', 'bool'])
def test_f144(nexus_template_with_streamed_log, shape, dtype):
def test_f144_merge(nexus_template_with_streamed_log, shape, dtype):
modules = collect_streaming_modules(nexus_template_with_streamed_log)
f144_modules = {
key: value for key, value in modules.items() if key.module_type == 'f144'
Expand All @@ -370,6 +395,86 @@ def test_f144(nexus_template_with_streamed_log, shape, dtype):
assert values['attributes'][0]['values'] == 'km'


def test_f144_merge_no_children_raises():
key = StreamModuleKey(module_type='f144', topic='', source='')
wrong_value = StreamModuleValue(
path=('',),
parent={'children': []},
dtype='int32',
value_units='km',
)
with pytest.raises(
InvalidNexusStructureError,
match="Group containing f144 module should have exactly one child",
):
merge_message_into_nexus_store(
module_key=key,
module_spec=wrong_value,
nexus_store={},
data={},
)


def test_f144_merge_too_many_children_raises():
key = StreamModuleKey(module_type='f144', topic='', source='')
wrong_value = StreamModuleValue(
path=('',),
parent={'children': [{}, {}]},
dtype='int32',
value_units='km',
)
with pytest.raises(
InvalidNexusStructureError,
match="Group containing f144 module should have exactly one child",
):
merge_message_into_nexus_store(
module_key=key,
module_spec=wrong_value,
nexus_store={},
data={},
)


def test_f144_merge_missing_dtype_raises():
key = StreamModuleKey(module_type='f144', topic='', source='')
wrong_value = StreamModuleValue(
path=('',),
parent={'children': [{}]},
dtype=None,
value_units='km',
)
with pytest.raises(
InvalidNexusStructureError,
match="f144 module spec should have dtype and value_units",
):
merge_message_into_nexus_store(
module_key=key,
module_spec=wrong_value,
nexus_store={},
data={},
)


def test_f144_merge_missing_value_units_raises():
key = StreamModuleKey(module_type='f144', topic='', source='')
wrong_value = StreamModuleValue(
path=('',),
parent={'children': [{}]},
dtype='int32',
value_units=None,
)
with pytest.raises(
InvalidNexusStructureError,
match="f144 module spec should have dtype and value_units",
):
merge_message_into_nexus_store(
module_key=key,
module_spec=wrong_value,
nexus_store={},
data={},
)


@pytest.fixture()
def nexus_template_with_streamed_tdct():
return {
Expand Down Expand Up @@ -401,7 +506,7 @@ def tdct_event_generator():
max_last_timestamp = timestamps.max()


def test_tdct(nexus_template_with_streamed_tdct: dict):
def test_tdct_merge(nexus_template_with_streamed_tdct: dict):
modules = collect_streaming_modules(nexus_template_with_streamed_tdct)
tdct_modules = {
key: value for key, value in modules.items() if key.module_type == 'tdct'
Expand All @@ -423,15 +528,3 @@ def test_tdct(nexus_template_with_streamed_tdct: dict):
assert np.issubdtype(
tdct['config']['values'].dtype, np.dtype(tdct['config']['dtype'])
)


@pytest.fixture()
def nexus_template_with_mixed_streams(
nexus_template_with_streamed_log, nexus_template_with_streamed_tdct
):
return {
"children": [
nexus_template_with_streamed_log,
nexus_template_with_streamed_tdct,
],
}
14 changes: 14 additions & 0 deletions tests/applications/streaming_module_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp)

import json
import pathlib

import pytest

from beamlime.applications._nexus_helpers import (
Expand Down Expand Up @@ -28,6 +31,15 @@ def _make_group_with_module_place_holder(
}


def test_invalid_nexus_template_multiple_module_placeholders() -> None:
with open(pathlib.Path(__file__).parent / "multiple_modules_datagroup.json") as f:
with pytest.raises(
InvalidNexusStructureError,
match="Group containing ev44 module should have exactly one child",
):
collect_streaming_modules(json.load(f))


def test_collect_streaming_modules_invalid_missing_topic_raises() -> None:
invalid_structure = {
"children": [
Expand Down Expand Up @@ -122,11 +134,13 @@ def test_collect_streaming_modules_nxlogs(ymir: dict) -> None:
"source": "delay_source_chopper",
"topic": "ymir_motion",
"dtype": "double",
"value_units": "s",
},
}
],
},
dtype="double",
value_units="s",
),
StreamModuleKey(
"f144", "ymir_motion", "YMIR-SEE:SE-LS336-004:KRDG0"
Expand Down
Loading

0 comments on commit a893cec

Please sign in to comment.