Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions src/nipanel/_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
IntCollectionConverter,
StrCollectionConverter,
)
from nipanel.converters.protobuf_types import DoubleAnalogWaveformConverter, ScalarConverter

_logger = logging.getLogger(__name__)

Expand All @@ -38,6 +39,9 @@
FloatCollectionConverter(),
IntCollectionConverter(),
StrCollectionConverter(),
# Protobuf Types
DoubleAnalogWaveformConverter(),
ScalarConverter(),
]

_CONVERTIBLE_COLLECTION_TYPES = {
Expand Down
159 changes: 108 additions & 51 deletions tests/unit/test_convert.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
from typing import Any, Type, Union
from typing import Any, Union

import numpy as np
import pytest
from google.protobuf import any_pb2, wrappers_pb2
from google.protobuf.message import Message
from ni.protobuf.types.scalar_pb2 import ScalarData
from ni.pythonpanel.v1 import python_panel_types_pb2
from ni_measurement_plugin_sdk_service._internal.stubs.ni.protobuf.types.waveform_pb2 import (
DoubleAnalogWaveform,
)
from nitypes.scalar import Scalar
from nitypes.waveform import AnalogWaveform
from typing_extensions import TypeAlias

import nipanel._convert
Expand All @@ -25,51 +33,6 @@
]


# ========================================================
# Python to Protobuf
# ========================================================
@pytest.mark.parametrize(
"proto_type, default_value, expected_value",
[
(wrappers_pb2.BoolValue, False, True),
(wrappers_pb2.BytesValue, b"", b"mystr"),
(wrappers_pb2.DoubleValue, 0.0, 456.2),
(wrappers_pb2.Int64Value, 0, 123),
(wrappers_pb2.StringValue, "", "mystr"),
],
)
def test___python_builtin_scalar___to_any___valid_wrapperpb2_value(
proto_type: Type[_AnyWrappersPb2], default_value: Any, expected_value: Any
) -> None:
result = nipanel._convert.to_any(expected_value)
unpack_dest = proto_type(value=default_value)
_assert_any_and_unpack(result, unpack_dest)

assert isinstance(unpack_dest, proto_type)
assert unpack_dest.value == expected_value


@pytest.mark.parametrize(
"proto_type, default_value, expected_value",
[
(python_panel_types_pb2.BoolCollection, [False, False, False], [True, True, True]),
(python_panel_types_pb2.ByteStringCollection, [b"", b"", b""], [b"a", b"b", b"c"]),
(python_panel_types_pb2.FloatCollection, [0.0, 0.0, 0.0], [1.0, 2.0, 3.0]),
(python_panel_types_pb2.IntCollection, [0, 0, 0], [1, 2, 3]),
(python_panel_types_pb2.StringCollection, ["", "", ""], ["a", "b", "c"]),
],
)
def test___python_panel_collection___to_any___valid_paneltype_value(
proto_type: Type[_AnyPanelPbTypes], default_value: Any, expected_value: Any
) -> None:
result = nipanel._convert.to_any(expected_value)
unpack_dest = proto_type(values=default_value)
_assert_any_and_unpack(result, unpack_dest)

assert isinstance(unpack_dest, proto_type)
assert unpack_dest.values == expected_value


# ========================================================
# _get_best_matching_type() tests
# ========================================================
Expand Down Expand Up @@ -116,7 +79,52 @@ def test___various_python_objects___get_best_matching_type___returns_correct_typ


# ========================================================
# Protobuf to Python
# Built-in Types: Python to Protobuf
# ========================================================
@pytest.mark.parametrize(
"proto_type, default_value, expected_value",
[
(wrappers_pb2.BoolValue, False, True),
(wrappers_pb2.BytesValue, b"", b"mystr"),
(wrappers_pb2.DoubleValue, 0.0, 456.2),
(wrappers_pb2.Int64Value, 0, 123),
(wrappers_pb2.StringValue, "", "mystr"),
],
)
def test___python_builtin_scalar___to_any___valid_wrapperpb2_value(
proto_type: type[_AnyWrappersPb2], default_value: Any, expected_value: Any
) -> None:
result = nipanel._convert.to_any(expected_value)
unpack_dest = proto_type(value=default_value)
_assert_any_and_unpack(result, unpack_dest)

assert isinstance(unpack_dest, proto_type)
assert unpack_dest.value == expected_value


@pytest.mark.parametrize(
"proto_type, default_value, expected_value",
[
(python_panel_types_pb2.BoolCollection, [False, False, False], [True, True, True]),
(python_panel_types_pb2.ByteStringCollection, [b"", b"", b""], [b"a", b"b", b"c"]),
(python_panel_types_pb2.FloatCollection, [0.0, 0.0, 0.0], [1.0, 2.0, 3.0]),
(python_panel_types_pb2.IntCollection, [0, 0, 0], [1, 2, 3]),
(python_panel_types_pb2.StringCollection, ["", "", ""], ["a", "b", "c"]),
],
)
def test___python_panel_collection___to_any___valid_paneltype_value(
proto_type: type[_AnyPanelPbTypes], default_value: Any, expected_value: Any
) -> None:
result = nipanel._convert.to_any(expected_value)
unpack_dest = proto_type(values=default_value)
_assert_any_and_unpack(result, unpack_dest)

assert isinstance(unpack_dest, proto_type)
assert unpack_dest.values == expected_value


# ========================================================
# Built-in Types: Protobuf to Python
# ========================================================
@pytest.mark.parametrize(
"proto_type, expected_value",
Expand All @@ -129,7 +137,7 @@ def test___various_python_objects___get_best_matching_type___returns_correct_typ
],
)
def test___wrapperpb2_value___from_any___valid_python_value(
proto_type: Type[_AnyWrappersPb2], expected_value: Any
proto_type: type[_AnyWrappersPb2], expected_value: Any
) -> None:
pb_value = proto_type(value=expected_value)
packed_any = _pack_into_any(pb_value)
Expand All @@ -151,7 +159,7 @@ def test___wrapperpb2_value___from_any___valid_python_value(
],
)
def test___paneltype_value___from_any___valid_python_value(
proto_type: Type[_AnyPanelPbTypes], expected_value: Any
proto_type: type[_AnyPanelPbTypes], expected_value: Any
) -> None:
pb_value = proto_type(values=expected_value)
packed_any = _pack_into_any(pb_value)
Expand All @@ -162,15 +170,64 @@ def test___paneltype_value___from_any___valid_python_value(
assert result == expected_value


# ========================================================
# Protobuf Types: Python to Protobuf
# ========================================================
def test___python_scalar_object___to_any___valid_scalar_data_value() -> None:
scalar_obj = Scalar(1.0, "amps")
result = nipanel._convert.to_any(scalar_obj)
unpack_dest = ScalarData()
_assert_any_and_unpack(result, unpack_dest)

assert isinstance(unpack_dest, ScalarData)
assert unpack_dest.double_value == 1.0
assert unpack_dest.units == "amps"


def test___python_analog_waveform___to_any___valid_double_analog_waveform() -> None:
wfm_obj = AnalogWaveform(3)
result = nipanel._convert.to_any(wfm_obj)
unpack_dest = DoubleAnalogWaveform()
_assert_any_and_unpack(result, unpack_dest)

assert isinstance(unpack_dest, DoubleAnalogWaveform)
assert list(unpack_dest.y_data) == [0.0, 0.0, 0.0]


# ========================================================
# Protobuf Types: Protobuf to Python
# ========================================================
def test___scalar_data___from_any___valid_python_scalar_object() -> None:
pb_value = ScalarData(units="amps", double_value=1.0)
packed_any = _pack_into_any(pb_value)

result = nipanel._convert.from_any(packed_any)

assert isinstance(result, Scalar)
assert result.value == 1.0
assert result.units == "amps"


def test___double_analog_waveform___from_any___valid_python_analog_waveform() -> None:
pb_value = DoubleAnalogWaveform(y_data=[0.0, 0.0, 0.0])
packed_any = _pack_into_any(pb_value)

result = nipanel._convert.from_any(packed_any)

assert isinstance(result, AnalogWaveform)
assert result.sample_count == result.capacity == len(result.raw_data) == 3
assert result.dtype == np.float64


# ========================================================
# Pack/Unpack Helpers
# ========================================================
def _assert_any_and_unpack(packed_message: any_pb2.Any, unpack_destination: Any) -> None:
def _assert_any_and_unpack(packed_message: any_pb2.Any, unpack_destination: Message) -> None:
assert isinstance(packed_message, any_pb2.Any)
assert packed_message.Unpack(unpack_destination)


def _pack_into_any(proto_value: Union[_AnyWrappersPb2, _AnyPanelPbTypes]) -> any_pb2.Any:
def _pack_into_any(proto_value: Message) -> any_pb2.Any:
as_any = any_pb2.Any()
as_any.Pack(proto_value)
return as_any