diff --git a/packages/service/ni_measurement_plugin_sdk_service/pin_map/__init__.py b/packages/service/ni_measurement_plugin_sdk_service/pin_map/__init__.py new file mode 100644 index 000000000..ef52a74a8 --- /dev/null +++ b/packages/service/ni_measurement_plugin_sdk_service/pin_map/__init__.py @@ -0,0 +1,5 @@ +"""Public API for accessing the NI Pin Map Service.""" + +from ni_measurement_plugin_sdk_service.pin_map._client import PinMapClient + +__all__ = ["PinMapClient"] diff --git a/packages/service/tests/utilities/pin_map_client.py b/packages/service/ni_measurement_plugin_sdk_service/pin_map/_client.py similarity index 88% rename from packages/service/tests/utilities/pin_map_client.py rename to packages/service/ni_measurement_plugin_sdk_service/pin_map/_client.py index 624516462..6f964e776 100644 --- a/packages/service/tests/utilities/pin_map_client.py +++ b/packages/service/ni_measurement_plugin_sdk_service/pin_map/_client.py @@ -1,4 +1,4 @@ -"""Client for accessing the measurement pin map service.""" +"""Client for accessing the NI Pin Map Service.""" import logging import pathlib @@ -21,7 +21,7 @@ class PinMapClient(object): - """Client for accessing the measurement pin map service.""" + """Client for accessing the NI Pin Map Service.""" def __init__( self, @@ -30,7 +30,15 @@ def __init__( grpc_channel: Optional[grpc.Channel] = None, grpc_channel_pool: Optional[GrpcChannelPool] = None ) -> None: - """Initialize the pin map client.""" + """Initialize the pin map client. + + Args: + discovery_client: An optional discovery client (recommended). + + grpc_channel: An optional pin map gRPC channel. + + grpc_channel_pool: An optional gRPC channel pool (recommended). + """ self._initialization_lock = threading.Lock() self._discovery_client = discovery_client self._grpc_channel_pool = grpc_channel_pool @@ -74,7 +82,7 @@ def update_pin_map(self, pin_map_path: Union[str, pathlib.Path]) -> str: # By convention, the pin map id is the .pinmap file path. request = pin_map_service_pb2.UpdatePinMapFromXmlRequest( pin_map_id=str(pin_map_path), - pin_map_xml=pathlib.Path(pin_map_path).read_text(encoding="utf-8"), + pin_map_xml=pathlib.Path(pin_map_path).read_text(encoding="utf-8-sig"), ) response = self._get_stub().UpdatePinMapFromXml(request) return response.pin_map_id diff --git a/packages/service/tests/acceptance/test_logging.py b/packages/service/tests/acceptance/test_logging.py index bc012a493..ec5745160 100644 --- a/packages/service/tests/acceptance/test_logging.py +++ b/packages/service/tests/acceptance/test_logging.py @@ -1,4 +1,5 @@ import logging +import pathlib import re from typing import Generator @@ -12,6 +13,7 @@ ) from ni_measurement_plugin_sdk_service.discovery import DiscoveryClient from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from ni_measurement_plugin_sdk_service.session_management import SessionManagementClient from tests.acceptance.test_streaming_data_measurement import ( _get_configuration_parameters as get_streaming_data_configuration_parameters, @@ -37,6 +39,21 @@ def test___discovery_client___call___client_call_logged( assert f"gRPC client call complete: {method_name}" in debug_messages +def test___pin_map_client___call___client_call_logged( + caplog: LogCaptureFixture, + pin_map_client: PinMapClient, + pin_map_directory: pathlib.Path, +) -> None: + with caplog.at_level(logging.DEBUG): + pin_map_path = pin_map_directory / "1Smu1ChannelGroup2Pin2Site.pinmap" + _ = pin_map_client.update_pin_map(pin_map_path) + + method_name = "/ni.measurementlink.pinmap.v1.PinMapService/UpdatePinMapFromXml" + debug_messages = [r.message for r in caplog.records if r.levelno == logging.DEBUG] + assert f"gRPC client call starting: {method_name}" in debug_messages + assert f"gRPC client call complete: {method_name}" in debug_messages + + def test___session_management_client___call___client_call_logged( caplog: LogCaptureFixture, request: FixtureRequest ) -> None: diff --git a/packages/service/tests/acceptance/test_nidaqmx_measurement.py b/packages/service/tests/acceptance/test_nidaqmx_measurement.py index 98938fe19..79bdffe41 100644 --- a/packages/service/tests/acceptance/test_nidaqmx_measurement.py +++ b/packages/service/tests/acceptance/test_nidaqmx_measurement.py @@ -13,8 +13,8 @@ PinMapContext, ) from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from tests.utilities.measurements import nidaqmx_measurement -from tests.utilities.pin_map_client import PinMapClient from tests.utilities.stubs.nidaqmx.types_pb2 import Configurations, Outputs _SITE = 0 diff --git a/packages/service/tests/acceptance/test_nidcpower_measurement.py b/packages/service/tests/acceptance/test_nidcpower_measurement.py index ebffed8ff..86ab5e219 100644 --- a/packages/service/tests/acceptance/test_nidcpower_measurement.py +++ b/packages/service/tests/acceptance/test_nidcpower_measurement.py @@ -13,8 +13,8 @@ PinMapContext, ) from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from tests.utilities.measurements import nidcpower_measurement -from tests.utilities.pin_map_client import PinMapClient from tests.utilities.stubs.nidcpower.types_pb2 import Configurations, Outputs _SITE = 0 diff --git a/packages/service/tests/acceptance/test_nidigital_measurement.py b/packages/service/tests/acceptance/test_nidigital_measurement.py index 7b92fa5d2..1994839df 100644 --- a/packages/service/tests/acceptance/test_nidigital_measurement.py +++ b/packages/service/tests/acceptance/test_nidigital_measurement.py @@ -13,8 +13,8 @@ PinMapContext, ) from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from tests.utilities.measurements import nidigital_measurement -from tests.utilities.pin_map_client import PinMapClient from tests.utilities.stubs.nidigital.types_pb2 import Configurations, Outputs diff --git a/packages/service/tests/acceptance/test_nidmm_measurement.py b/packages/service/tests/acceptance/test_nidmm_measurement.py index 77bb6b5a8..bb6f12158 100644 --- a/packages/service/tests/acceptance/test_nidmm_measurement.py +++ b/packages/service/tests/acceptance/test_nidmm_measurement.py @@ -13,8 +13,8 @@ PinMapContext, ) from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from tests.utilities.measurements import nidmm_measurement -from tests.utilities.pin_map_client import PinMapClient from tests.utilities.stubs.nidmm.types_pb2 import Configurations, Outputs _SITE = 0 diff --git a/packages/service/tests/acceptance/test_nifgen_measurement.py b/packages/service/tests/acceptance/test_nifgen_measurement.py index a56ba535a..d402896b1 100644 --- a/packages/service/tests/acceptance/test_nifgen_measurement.py +++ b/packages/service/tests/acceptance/test_nifgen_measurement.py @@ -13,8 +13,8 @@ PinMapContext, ) from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from tests.utilities.measurements import nifgen_measurement -from tests.utilities.pin_map_client import PinMapClient from tests.utilities.stubs.nifgen.types_pb2 import Configurations, Outputs _SITE = 0 diff --git a/packages/service/tests/acceptance/test_niscope_measurement.py b/packages/service/tests/acceptance/test_niscope_measurement.py index f05683abf..2dca3c8d2 100644 --- a/packages/service/tests/acceptance/test_niscope_measurement.py +++ b/packages/service/tests/acceptance/test_niscope_measurement.py @@ -13,8 +13,8 @@ PinMapContext, ) from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from tests.utilities.measurements import niscope_measurement -from tests.utilities.pin_map_client import PinMapClient from tests.utilities.stubs.niscope.types_pb2 import Configurations, Outputs _SITE = 0 diff --git a/packages/service/tests/acceptance/test_niswitch_measurement.py b/packages/service/tests/acceptance/test_niswitch_measurement.py index 67a3c1145..746788515 100644 --- a/packages/service/tests/acceptance/test_niswitch_measurement.py +++ b/packages/service/tests/acceptance/test_niswitch_measurement.py @@ -13,8 +13,8 @@ PinMapContext, ) from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from tests.utilities.measurements import niswitch_measurement -from tests.utilities.pin_map_client import PinMapClient from tests.utilities.stubs.niswitch.types_pb2 import Configurations, Outputs _SITE = 0 diff --git a/packages/service/tests/acceptance/test_niswitch_multiplexer_measurement.py b/packages/service/tests/acceptance/test_niswitch_multiplexer_measurement.py index e26f37afb..66f027567 100644 --- a/packages/service/tests/acceptance/test_niswitch_multiplexer_measurement.py +++ b/packages/service/tests/acceptance/test_niswitch_multiplexer_measurement.py @@ -13,8 +13,8 @@ PinMapContext, ) from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from tests.utilities.measurements import niswitch_multiplexer_measurement -from tests.utilities.pin_map_client import PinMapClient from tests.utilities.stubs.niswitchmultiplexer.types_pb2 import Configurations, Outputs _SITE = 0 diff --git a/packages/service/tests/acceptance/test_session_management.py b/packages/service/tests/acceptance/test_session_management.py index e63def482..ca679a3e7 100644 --- a/packages/service/tests/acceptance/test_session_management.py +++ b/packages/service/tests/acceptance/test_session_management.py @@ -14,9 +14,9 @@ PinMapContext, ) from ni_measurement_plugin_sdk_service.measurement.service import MeasurementService +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from tests.utilities.discovery_service_process import DiscoveryServiceProcess from tests.utilities.measurements import pin_aware_measurement -from tests.utilities.pin_map_client import PinMapClient from tests.utilities.stubs.pinaware.types_pb2 import Configurations, Outputs diff --git a/packages/service/tests/assets/unit/pin_map/1Smu1ChannelGroup1Pin1Site.pinmap b/packages/service/tests/assets/unit/pin_map/1Smu1ChannelGroup1Pin1Site.pinmap new file mode 100644 index 000000000..f16b4fd9e --- /dev/null +++ b/packages/service/tests/assets/unit/pin_map/1Smu1ChannelGroup1Pin1Site.pinmap @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/service/tests/conftest.py b/packages/service/tests/conftest.py index a18d6cbc5..f0a91228f 100644 --- a/packages/service/tests/conftest.py +++ b/packages/service/tests/conftest.py @@ -23,9 +23,9 @@ GrpcChannelPool, MeasurementService, ) +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from ni_measurement_plugin_sdk_service.session_management import SessionManagementClient from tests.utilities.discovery_service_process import DiscoveryServiceProcess -from tests.utilities.pin_map_client import PinMapClient @pytest.fixture(scope="module") diff --git a/packages/service/tests/integration/session_management/test_nidaqmx_reservation.py b/packages/service/tests/integration/session_management/test_nidaqmx_reservation.py index 7dea03ef7..9e9a2bd15 100644 --- a/packages/service/tests/integration/session_management/test_nidaqmx_reservation.py +++ b/packages/service/tests/integration/session_management/test_nidaqmx_reservation.py @@ -3,12 +3,12 @@ import pytest +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from ni_measurement_plugin_sdk_service.session_management import ( PinMapContext, SessionManagementClient, ) from tests.utilities.connection_subset import ConnectionSubset, get_connection_subset -from tests.utilities.pin_map_client import PinMapClient _SITE = 0 diff --git a/packages/service/tests/integration/session_management/test_nidcpower_reservation.py b/packages/service/tests/integration/session_management/test_nidcpower_reservation.py index 2f1e60c90..8d4a9e17c 100644 --- a/packages/service/tests/integration/session_management/test_nidcpower_reservation.py +++ b/packages/service/tests/integration/session_management/test_nidcpower_reservation.py @@ -3,12 +3,12 @@ import pytest +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from ni_measurement_plugin_sdk_service.session_management import ( PinMapContext, SessionManagementClient, ) from tests.utilities.connection_subset import ConnectionSubset, get_connection_subset -from tests.utilities.pin_map_client import PinMapClient _SITE = 0 diff --git a/packages/service/tests/integration/session_management/test_nidigital_reservation.py b/packages/service/tests/integration/session_management/test_nidigital_reservation.py index c45f7d5b2..c99440aea 100644 --- a/packages/service/tests/integration/session_management/test_nidigital_reservation.py +++ b/packages/service/tests/integration/session_management/test_nidigital_reservation.py @@ -3,12 +3,12 @@ import pytest +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from ni_measurement_plugin_sdk_service.session_management import ( PinMapContext, SessionManagementClient, ) from tests.utilities.connection_subset import ConnectionSubset, get_connection_subset -from tests.utilities.pin_map_client import PinMapClient def test___single_session_reserved___initialize_nidigital_session___creates_single_session( diff --git a/packages/service/tests/integration/session_management/test_nidmm_reservation.py b/packages/service/tests/integration/session_management/test_nidmm_reservation.py index 818fedb75..28795cef7 100644 --- a/packages/service/tests/integration/session_management/test_nidmm_reservation.py +++ b/packages/service/tests/integration/session_management/test_nidmm_reservation.py @@ -3,12 +3,12 @@ import pytest +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from ni_measurement_plugin_sdk_service.session_management import ( PinMapContext, SessionManagementClient, ) from tests.utilities.connection_subset import ConnectionSubset, get_connection_subset -from tests.utilities.pin_map_client import PinMapClient _SITE = 0 diff --git a/packages/service/tests/integration/session_management/test_nifgen_reservation.py b/packages/service/tests/integration/session_management/test_nifgen_reservation.py index 8a39192b2..d2fb003fd 100644 --- a/packages/service/tests/integration/session_management/test_nifgen_reservation.py +++ b/packages/service/tests/integration/session_management/test_nifgen_reservation.py @@ -3,12 +3,12 @@ import pytest +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from ni_measurement_plugin_sdk_service.session_management import ( PinMapContext, SessionManagementClient, ) from tests.utilities.connection_subset import ConnectionSubset, get_connection_subset -from tests.utilities.pin_map_client import PinMapClient _SITE = 0 diff --git a/packages/service/tests/integration/session_management/test_niscope_reservation.py b/packages/service/tests/integration/session_management/test_niscope_reservation.py index 6c6311429..13fc90056 100644 --- a/packages/service/tests/integration/session_management/test_niscope_reservation.py +++ b/packages/service/tests/integration/session_management/test_niscope_reservation.py @@ -3,12 +3,12 @@ import pytest +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from ni_measurement_plugin_sdk_service.session_management import ( PinMapContext, SessionManagementClient, ) from tests.utilities.connection_subset import ConnectionSubset, get_connection_subset -from tests.utilities.pin_map_client import PinMapClient _SITE = 0 diff --git a/packages/service/tests/integration/session_management/test_niswitch_multiplexer_reservation.py b/packages/service/tests/integration/session_management/test_niswitch_multiplexer_reservation.py index a4d745e6b..f3a2fb286 100644 --- a/packages/service/tests/integration/session_management/test_niswitch_multiplexer_reservation.py +++ b/packages/service/tests/integration/session_management/test_niswitch_multiplexer_reservation.py @@ -4,6 +4,7 @@ import niswitch import pytest +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from ni_measurement_plugin_sdk_service.session_management import ( PinMapContext, SessionManagementClient, @@ -12,7 +13,6 @@ ConnectionSubset, get_connection_subset_with_multiplexer, ) -from tests.utilities.pin_map_client import PinMapClient _SITE = 0 diff --git a/packages/service/tests/integration/session_management/test_niswitch_reservation.py b/packages/service/tests/integration/session_management/test_niswitch_reservation.py index fd7c7273f..1e49711db 100644 --- a/packages/service/tests/integration/session_management/test_niswitch_reservation.py +++ b/packages/service/tests/integration/session_management/test_niswitch_reservation.py @@ -3,12 +3,12 @@ import pytest +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from ni_measurement_plugin_sdk_service.session_management import ( PinMapContext, SessionManagementClient, ) from tests.utilities.connection_subset import ConnectionSubset, get_connection_subset -from tests.utilities.pin_map_client import PinMapClient _SITE = 0 diff --git a/packages/service/tests/integration/session_management/test_reservation.py b/packages/service/tests/integration/session_management/test_reservation.py index 09f5a26ba..19410e1d8 100644 --- a/packages/service/tests/integration/session_management/test_reservation.py +++ b/packages/service/tests/integration/session_management/test_reservation.py @@ -3,6 +3,7 @@ import pathlib from contextlib import ExitStack +from ni_measurement_plugin_sdk_service.pin_map import PinMapClient from ni_measurement_plugin_sdk_service.session_management import ( INSTRUMENT_TYPE_NI_DCPOWER, INSTRUMENT_TYPE_NI_RELAY_DRIVER, @@ -15,7 +16,6 @@ get_connection_subset, get_connection_subset_with_multiplexer, ) -from tests.utilities.pin_map_client import PinMapClient _PIN_MAP_A = "PinMapA_3Instruments_3DutPins_2SystemPins_2Sites.pinmap" _PIN_MAP_A_PIN_NAMES = ["A", "B", "C", "S1", "S2"] diff --git a/packages/service/tests/unit/conftest.py b/packages/service/tests/unit/conftest.py index a0e555558..2456b4da9 100644 --- a/packages/service/tests/unit/conftest.py +++ b/packages/service/tests/unit/conftest.py @@ -1,5 +1,6 @@ """Test fixtures for unit tests.""" +import pathlib from typing import Generator, cast from unittest.mock import Mock @@ -94,3 +95,9 @@ def session_management_client( def single_session_reservation(mocker: MockerFixture) -> Mock: """Test fixture that creates a mock SingleSessionReservation.""" return mocker.create_autospec(SingleSessionReservation) + + +@pytest.fixture +def pin_map_directory(test_assets_directory: pathlib.Path) -> pathlib.Path: + """Test fixture that returns the pin map directory.""" + return test_assets_directory / "unit" / "pin_map" diff --git a/packages/service/tests/unit/test_pin_map_client.py b/packages/service/tests/unit/test_pin_map_client.py new file mode 100644 index 000000000..9555b58c8 --- /dev/null +++ b/packages/service/tests/unit/test_pin_map_client.py @@ -0,0 +1,78 @@ +"""Contains tests to validate the pin_map_client.py. +""" + +import pathlib +from typing import cast +from unittest.mock import Mock + +import grpc +import pytest +from pytest_mock import MockerFixture + +from ni_measurement_plugin_sdk_service._internal.stubs.ni.measurementlink.pinmap.v1.pin_map_service_pb2 import ( + PinMap, + UpdatePinMapFromXmlRequest, +) +from ni_measurement_plugin_sdk_service._internal.stubs.ni.measurementlink.pinmap.v1.pin_map_service_pb2_grpc import ( + PinMapServiceStub, +) +from ni_measurement_plugin_sdk_service.discovery import DiscoveryClient +from ni_measurement_plugin_sdk_service.grpc.channelpool import GrpcChannelPool +from ni_measurement_plugin_sdk_service.pin_map._client import PinMapClient + + +def test___valid_pin_map_file___register_pin_map___returns_pin_map_id( + pin_map_client: PinMapClient, + pin_map_stub: Mock, + pin_map_directory: pathlib.Path, +) -> None: + pin_map_path = str(pin_map_directory / "1Smu1ChannelGroup1Pin1Site.pinmap") + pin_map_stub.UpdatePinMapFromXml.return_value = PinMap(pin_map_id=pin_map_path) + + registered_pin_map_id = pin_map_client.update_pin_map(pin_map_path) + + pin_map_stub.UpdatePinMapFromXml.assert_called_once() + request: UpdatePinMapFromXmlRequest = pin_map_stub.UpdatePinMapFromXml.call_args.args[0] + assert request.pin_map_id == pin_map_path + assert request.pin_map_xml == _get_pin_map_file_contents(pin_map_path) + assert registered_pin_map_id == pin_map_path + + +def test___invalid_pin_map_file_path___register_pin_map___raises_file_not_found_error( + pin_map_client: PinMapClient, +) -> None: + pin_map_path = "InvalidPinMap.pinmap" + + with pytest.raises(FileNotFoundError): + _ = pin_map_client.update_pin_map(pin_map_path) + + +def _get_pin_map_file_contents(pin_map_path: str) -> str: + return pathlib.Path(pin_map_path).read_text(encoding="utf-8-sig") + + +@pytest.fixture +def pin_map_client( + discovery_client: Mock, + grpc_channel_pool: Mock, + mocker: MockerFixture, + pin_map_stub: Mock, +) -> PinMapClient: + """Create a Client with a mock PinMapServiceStub.""" + mocker.patch( + "ni_measurement_plugin_sdk_service.pin_map.PinMapClient._get_stub", + return_value=pin_map_stub, + ) + client = PinMapClient( + discovery_client=cast(DiscoveryClient, discovery_client), + grpc_channel_pool=cast(GrpcChannelPool, grpc_channel_pool), + ) + return client + + +@pytest.fixture +def pin_map_stub(mocker: MockerFixture) -> Mock: + """Create a mock PinMapServiceStub.""" + stub = mocker.create_autospec(PinMapServiceStub) + stub.UpdatePinMapFromXml = mocker.create_autospec(grpc.UnaryUnaryMultiCallable) + return stub