From 707f2fb0dd96624917e50e7b4b7c8329b09ab1d3 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Fri, 3 Nov 2023 16:43:17 +0530 Subject: [PATCH 01/19] tests: set platform to add integration & acceptance tests for nidigital --- .../acceptance/test_nidigital_measurement.py | 0 .../2Digital2Group4Pin1Site.pinmap | 23 ++++ .../session_management/Pattern.digipat | Bin 0 -> 4188 bytes .../session_management/PinLevels.digilevels | 51 ++++++++ .../session_management/Specifications.specs | 16 +++ .../session_management/Timing.digitiming | 57 +++++++++ .../stubs/nidigital_measurement/__init__.py | 1 + .../stubs/nidigital_measurement/types.proto | 16 +++ .../stubs/nidigital_measurement/types_pb2.py | 27 +++++ .../stubs/nidigital_measurement/types_pb2.pyi | 72 +++++++++++ .../nidigital_measurement/types_pb2_grpc.py | 4 + .../nidigital_measurement/types_pb2_grpc.pyi | 17 +++ .../test_nidigital_reservation.py | 0 .../NIDigitalMeasurement.serviceconfig | 19 +++ .../nidigital_measurement/__init__.py | 114 ++++++++++++++++++ 15 files changed, 417 insertions(+) create mode 100644 tests/acceptance/test_nidigital_measurement.py create mode 100644 tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap create mode 100644 tests/assets/acceptance/session_management/Pattern.digipat create mode 100644 tests/assets/acceptance/session_management/PinLevels.digilevels create mode 100644 tests/assets/acceptance/session_management/Specifications.specs create mode 100644 tests/assets/acceptance/session_management/Timing.digitiming create mode 100644 tests/assets/stubs/nidigital_measurement/__init__.py create mode 100644 tests/assets/stubs/nidigital_measurement/types.proto create mode 100644 tests/assets/stubs/nidigital_measurement/types_pb2.py create mode 100644 tests/assets/stubs/nidigital_measurement/types_pb2.pyi create mode 100644 tests/assets/stubs/nidigital_measurement/types_pb2_grpc.py create mode 100644 tests/assets/stubs/nidigital_measurement/types_pb2_grpc.pyi create mode 100644 tests/integration/session_management/test_nidigital_reservation.py create mode 100644 tests/utilities/nidigital_measurement/NIDigitalMeasurement.serviceconfig create mode 100644 tests/utilities/nidigital_measurement/__init__.py diff --git a/tests/acceptance/test_nidigital_measurement.py b/tests/acceptance/test_nidigital_measurement.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap b/tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap new file mode 100644 index 000000000..21b25bed6 --- /dev/null +++ b/tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/assets/acceptance/session_management/Pattern.digipat b/tests/assets/acceptance/session_management/Pattern.digipat new file mode 100644 index 0000000000000000000000000000000000000000..a3566238de9e248a218a8b903a5ef5b5ed0604ce GIT binary patch literal 4188 zcmcgvYi`p(5O$z|cnHc<2oRWG2B{GD-3kO$F^{OPsHuvCgeo`Pwl1+9wj0zFAdbKp z_{~qwz!^9K;sP+AcPHMqiIlVrgQj0R-ktqCvopIa2i5qr<2cb3$Jro+Fc#=HwD^}~ zfPO%eFcvQ1dD3d7>{ddhgiP?bE_P}%YN#kpFamOlR7I+uO@RSxg-x}UN*9kL%NBzn zZE_g*Fu+3_2CIgF?dPHK!+5~Y1sbp-I_eO;4LV8FUj2-d$OaqjXq|dONkLQZg-&ZDwT1lyleCtA;D0V!<7_o!ENJ)5uUNizF73R5bA~nBUqs4 zwPBHErJSsb-7Ff|`NtQHcZSDPgWX00Ck_OhS&$dx3fmOXE>)|gx!L)}YI(j~U9@}a z0=AiF%()HO{Cw4kO6iBc)PpLk6^P)$2p8N<)MZL~!-6 ziM-s!QTIQ>k{C9OVB>aX)5o~f3=>8$F{fKAL3GYlRj5Rk7F@qEh~;Pp%93$YG6>H- zY*r?6iP;dMO6hiE-{HA5c$Md7IHvcw8+k?JVNd;kH!s_q2d!APyY`|-Kz8>#C)+L3 zAyRbv3h5fDBK=JImGm3w57M8czexX(4v?Q4qz^2`N#ZWiMZWk}Qe$-=xj0wcV0b)x zGc%D}#&Vj|pxBK~fiX~)RL$>X^s*ML`0-#WFDuXkFzV+|G zlT#^F?1eYXd`9exo1}KLDkt + + + + + dc.vcc * 0.7 + 0 + 2.5 + 0.5 + 1.5 m + -1.5 m + 0 + 0 + HighZ + + + dc.vcc * 0.7 + 0 + 2.5 + 0.5 + 1.5 m + -1.5 m + 0 + 0 + HighZ + + + dc.vcc * 0.7 + 0 + 2.5 + 0.5 + 1.5 m + -1.5 m + 0 + 0 + HighZ + + + dc.vcc * 0.7 + 0 + 2.5 + 0.5 + 1.5 m + -1.5 m + 0 + 0 + HighZ + + + + \ No newline at end of file diff --git a/tests/assets/acceptance/session_management/Specifications.specs b/tests/assets/acceptance/session_management/Specifications.specs new file mode 100644 index 000000000..8792bc803 --- /dev/null +++ b/tests/assets/acceptance/session_management/Specifications.specs @@ -0,0 +1,16 @@ + + +
+ + 5 + V + +
+
+ + 1 / 1000000 + 1 MHz + +
+ +
\ No newline at end of file diff --git a/tests/assets/acceptance/session_management/Timing.digitiming b/tests/assets/acceptance/session_management/Timing.digitiming new file mode 100644 index 000000000..df69bee5d --- /dev/null +++ b/tests/assets/acceptance/session_management/Timing.digitiming @@ -0,0 +1,57 @@ + + + + + + ac.period + + + + 0 + 0 + ac.period + + + (3 * ac.period) / 4 + + Pattern + + + + ac.period / 2 + ac.period / 2 + ac.period + ac.period + + + (3 * ac.period) / 4 + + Pattern + + + + 0 + 0 + ac.period + + + (3 * ac.period) / 4 + + Pattern + + + + 0 + 0 + ac.period + + + (3 * ac.period) / 4 + + Pattern + + + + + + \ No newline at end of file diff --git a/tests/assets/stubs/nidigital_measurement/__init__.py b/tests/assets/stubs/nidigital_measurement/__init__.py new file mode 100644 index 000000000..10eddb732 --- /dev/null +++ b/tests/assets/stubs/nidigital_measurement/__init__.py @@ -0,0 +1 @@ +"""Auto generated gRPC files for nidigital test measurement.""" diff --git a/tests/assets/stubs/nidigital_measurement/types.proto b/tests/assets/stubs/nidigital_measurement/types.proto new file mode 100644 index 000000000..cec3c0e59 --- /dev/null +++ b/tests/assets/stubs/nidigital_measurement/types.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package ni.measurementlink.measurement.tests.nidigital_measurement; + +message Configurations { + repeated string relay_names = 1; + bool multi_session = 2; +} + +message Outputs { + repeated string session_names = 1; + repeated string resource_names = 2; + repeated string channel_lists = 3; + repeated string connected_channels = 4; + repeated int32 passing_sites = 5; + repeated int32 failing_sites = 6; +} \ No newline at end of file diff --git a/tests/assets/stubs/nidigital_measurement/types_pb2.py b/tests/assets/stubs/nidigital_measurement/types_pb2.py new file mode 100644 index 000000000..e2633ded6 --- /dev/null +++ b/tests/assets/stubs/nidigital_measurement/types_pb2.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: stubs/nidigital_measurement/types.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'stubs/nidigital_measurement/types.proto\x12:ni.measurementlink.measurement.tests.nidigital_measurement\"<\n\x0e\x43onfigurations\x12\x13\n\x0brelay_names\x18\x01 \x03(\t\x12\x15\n\rmulti_session\x18\x02 \x01(\x08\"\x99\x01\n\x07Outputs\x12\x15\n\rsession_names\x18\x01 \x03(\t\x12\x16\n\x0eresource_names\x18\x02 \x03(\t\x12\x15\n\rchannel_lists\x18\x03 \x03(\t\x12\x1a\n\x12\x63onnected_channels\x18\x04 \x03(\t\x12\x15\n\rpassing_sites\x18\x05 \x03(\x05\x12\x15\n\rfailing_sites\x18\x06 \x03(\x05\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'stubs.nidigital_measurement.types_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _CONFIGURATIONS._serialized_start=103 + _CONFIGURATIONS._serialized_end=163 + _OUTPUTS._serialized_start=166 + _OUTPUTS._serialized_end=319 +# @@protoc_insertion_point(module_scope) diff --git a/tests/assets/stubs/nidigital_measurement/types_pb2.pyi b/tests/assets/stubs/nidigital_measurement/types_pb2.pyi new file mode 100644 index 000000000..aaf224b5f --- /dev/null +++ b/tests/assets/stubs/nidigital_measurement/types_pb2.pyi @@ -0,0 +1,72 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class Configurations(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RELAY_NAMES_FIELD_NUMBER: builtins.int + MULTI_SESSION_FIELD_NUMBER: builtins.int + @property + def relay_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + multi_session: builtins.bool + def __init__( + self, + *, + relay_names: collections.abc.Iterable[builtins.str] | None = ..., + multi_session: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["multi_session", b"multi_session", "relay_names", b"relay_names"]) -> None: ... + +global___Configurations = Configurations + +@typing_extensions.final +class Outputs(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SESSION_NAMES_FIELD_NUMBER: builtins.int + RESOURCE_NAMES_FIELD_NUMBER: builtins.int + CHANNEL_LISTS_FIELD_NUMBER: builtins.int + CONNECTED_CHANNELS_FIELD_NUMBER: builtins.int + PASSING_SITES_FIELD_NUMBER: builtins.int + FAILING_SITES_FIELD_NUMBER: builtins.int + @property + def session_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + @property + def resource_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + @property + def channel_lists(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + @property + def connected_channels(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + @property + def passing_sites(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: ... + @property + def failing_sites(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: ... + def __init__( + self, + *, + session_names: collections.abc.Iterable[builtins.str] | None = ..., + resource_names: collections.abc.Iterable[builtins.str] | None = ..., + channel_lists: collections.abc.Iterable[builtins.str] | None = ..., + connected_channels: collections.abc.Iterable[builtins.str] | None = ..., + passing_sites: collections.abc.Iterable[builtins.int] | None = ..., + failing_sites: collections.abc.Iterable[builtins.int] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["channel_lists", b"channel_lists", "connected_channels", b"connected_channels", "failing_sites", b"failing_sites", "passing_sites", b"passing_sites", "resource_names", b"resource_names", "session_names", b"session_names"]) -> None: ... + +global___Outputs = Outputs diff --git a/tests/assets/stubs/nidigital_measurement/types_pb2_grpc.py b/tests/assets/stubs/nidigital_measurement/types_pb2_grpc.py new file mode 100644 index 000000000..2daafffeb --- /dev/null +++ b/tests/assets/stubs/nidigital_measurement/types_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/tests/assets/stubs/nidigital_measurement/types_pb2_grpc.pyi b/tests/assets/stubs/nidigital_measurement/types_pb2_grpc.pyi new file mode 100644 index 000000000..b13382f6a --- /dev/null +++ b/tests/assets/stubs/nidigital_measurement/types_pb2_grpc.pyi @@ -0,0 +1,17 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import abc +import collections.abc +import grpc +import grpc.aio +import typing + +_T = typing.TypeVar('_T') + +class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta): + ... + +class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore + ... diff --git a/tests/integration/session_management/test_nidigital_reservation.py b/tests/integration/session_management/test_nidigital_reservation.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/utilities/nidigital_measurement/NIDigitalMeasurement.serviceconfig b/tests/utilities/nidigital_measurement/NIDigitalMeasurement.serviceconfig new file mode 100644 index 000000000..80b796820 --- /dev/null +++ b/tests/utilities/nidigital_measurement/NIDigitalMeasurement.serviceconfig @@ -0,0 +1,19 @@ +{ + "services": [ + { + "displayName": "NI-Digital Measurement (Py)", + "serviceClass": "ni.tests.NIDigitalMeasurement_Python", + "descriptionUrl": "", + "providedInterfaces": [ + "ni.measurementlink.measurement.v1.MeasurementService", + "ni.measurementlink.measurement.v2.MeasurementService" + ], + "path": "start.bat", + "annotations": { + "ni/service.description": "NI-Digital MeasurementLink test service.", + "ni/service.collection": "NI.Tests", + "ni/service.tags": [] + } + } + ] +} \ No newline at end of file diff --git a/tests/utilities/nidigital_measurement/__init__.py b/tests/utilities/nidigital_measurement/__init__.py new file mode 100644 index 000000000..81b4da227 --- /dev/null +++ b/tests/utilities/nidigital_measurement/__init__.py @@ -0,0 +1,114 @@ +"""NI-Digital MeasurementLink test service.""" +import pathlib +from typing import Iterable, Sequence, Tuple, Union + +import nidigital + +import ni_measurementlink_service as nims + +service_directory = pathlib.Path(__file__).resolve().parent +measurement_service = nims.MeasurementService( + service_config_path=service_directory / "NIDigitalMeasurement.serviceconfig", + version="0.1.0.0", + ui_file_paths=[ + service_directory, + ], +) + + +@measurement_service.register_measurement +@measurement_service.configuration("pin_names", nims.DataType.StringArray1D, ["SPI_PINS"]) +@measurement_service.configuration("multi_session", nims.DataType.Boolean, False) +@measurement_service.output("session_names", nims.DataType.StringArray1D) +@measurement_service.output("resource_names", nims.DataType.StringArray1D) +@measurement_service.output("channel_lists", nims.DataType.StringArray1D) +@measurement_service.output("connected_channels", nims.DataType.StringArray1D) +@measurement_service.output("passing_sites", nims.DataType.Int32Array1D) +@measurement_service.output("failing_sites", nims.DataType.Int32Array1D) +def measure( + pin_names: Iterable[str], + multi_session: bool, +) -> Tuple[ + Iterable[str], Iterable[str], Iterable[str], Iterable[str], Iterable[str], Iterable[str] +]: + """NI-Digital MeasurementLink test service.""" + if multi_session: + with measurement_service.context.reserve_sessions(pin_names) as reservation: + with reservation.initialize_nidigital_sessions() as session_infos: + connections = reservation.get_nidigital_connections(pin_names) + assert all([session_info is not None for session_info in session_infos]) + passing_sites, failing_sites = _spi(session_infos) + + return ( + [session_info.session_name for session_info in session_infos], + [session_info.resource_name for session_info in session_infos], + [session_info.channel_list for session_info in session_infos], + [connection.channel_name for connection in connections], + passing_sites, + failing_sites, + ) + else: + with measurement_service.context.reserve_session(pin_names) as reservation: + with reservation.initialize_nidigital_session() as session_info: + connection = reservation.get_nidigital_connection(list(pin_names)[0]) + assert session_info is not None + passing_sites, failing_sites = _spi([session_info]) + + return ( + [session_info.session_name], + [session_info.resource_name], + [session_info.channel_list], + [connection.channel_name], + passing_sites, + failing_sites, + ) + + +def _spi( + session_infos: Sequence[nims.session_management.TypedSessionInformation[nidigital.Session]], +) -> Tuple: + test_assets_directory = ( + service_directory.parent.parent / "assets" / "acceptance" / "session_management" + ) + specifications_file_path = "Specifications.specs" + levels_file_path = "PinLevels.digilevels" + timing_file_path = "Timing.digitiming" + pattern_file_path = "Pattern.digipat" + pin_map_context = measurement_service.context.pin_map_context + passing_sites_list, failing_sites_list = [], [] + for session_info in session_infos: + session = session_info.session + selected_sites_string = ",".join(f"site{i}" for i in pin_map_context.sites or []) + selected_sites = session.sites[selected_sites_string] + session.load_pin_map(pin_map_context.pin_map_id) + session.load_specifications_levels_and_timing( + str(_resolve_relative_path(test_assets_directory, specifications_file_path)), + str(_resolve_relative_path(test_assets_directory, levels_file_path)), + str(_resolve_relative_path(test_assets_directory, timing_file_path)), + ) + session.load_pattern( + str(_resolve_relative_path(service_directory, pattern_file_path)), + ) + + levels_file_name = pathlib.Path(levels_file_path).stem + timing_file_name = pathlib.Path(timing_file_path).stem + selected_sites.apply_levels_and_timing(levels_file_name, timing_file_name) + selected_sites.burst_pattern(start_label="SPI_Pattern") + site_pass_fail = selected_sites.get_site_pass_fail() + passing_sites = [site for site, pass_fail in site_pass_fail.items() if pass_fail] + failing_sites = [site for site, pass_fail in site_pass_fail.items() if not pass_fail] + passing_sites_list.append(passing_sites) + failing_sites_list.append(failing_sites) + session.selected_function = nidigital.SelectedFunction.DISCONNECT + + return (passing_sites_list, failing_sites_list) + + +def _resolve_relative_path( + directory_path: pathlib.Path, file_path: Union[str, pathlib.Path] +) -> pathlib.Path: + file_path = pathlib.Path(file_path) + if file_path.is_absolute(): + return file_path + else: + return (directory_path / file_path).resolve() From c4c4377bd675adda9428911dfb96b862e6e6afa0 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Fri, 3 Nov 2023 16:44:14 +0530 Subject: [PATCH 02/19] tests: set platform to add integration & acceptance test for niswitch refactor: niswitch test measurement --- tests/acceptance/test_niswitch_measurement.py | 0 .../2Switch2Relay1Site.pinmap | 21 ++++++ .../stubs/niswitch_measurement/__init__.py | 1 + .../stubs/niswitch_measurement/types.proto | 14 ++++ .../stubs/niswitch_measurement/types_pb2.py | 27 ++++++++ .../stubs/niswitch_measurement/types_pb2.pyi | 64 ++++++++++++++++++ .../niswitch_measurement/types_pb2_grpc.py | 4 ++ .../niswitch_measurement/types_pb2_grpc.pyi | 17 +++++ .../test_niswitch_reservation.py | 0 .../NISwitchMeasurement.serviceconfig | 19 ++++++ .../niswitch_measurement/__init__.py | 67 +++++++++++++++++++ 11 files changed, 234 insertions(+) create mode 100644 tests/acceptance/test_niswitch_measurement.py create mode 100644 tests/assets/acceptance/session_management/2Switch2Relay1Site.pinmap create mode 100644 tests/assets/stubs/niswitch_measurement/__init__.py create mode 100644 tests/assets/stubs/niswitch_measurement/types.proto create mode 100644 tests/assets/stubs/niswitch_measurement/types_pb2.py create mode 100644 tests/assets/stubs/niswitch_measurement/types_pb2.pyi create mode 100644 tests/assets/stubs/niswitch_measurement/types_pb2_grpc.py create mode 100644 tests/assets/stubs/niswitch_measurement/types_pb2_grpc.pyi create mode 100644 tests/integration/session_management/test_niswitch_reservation.py create mode 100644 tests/utilities/niswitch_measurement/NISwitchMeasurement.serviceconfig create mode 100644 tests/utilities/niswitch_measurement/__init__.py diff --git a/tests/acceptance/test_niswitch_measurement.py b/tests/acceptance/test_niswitch_measurement.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/assets/acceptance/session_management/2Switch2Relay1Site.pinmap b/tests/assets/acceptance/session_management/2Switch2Relay1Site.pinmap new file mode 100644 index 000000000..cf9e89756 --- /dev/null +++ b/tests/assets/acceptance/session_management/2Switch2Relay1Site.pinmap @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/assets/stubs/niswitch_measurement/__init__.py b/tests/assets/stubs/niswitch_measurement/__init__.py new file mode 100644 index 000000000..aa66b3a5d --- /dev/null +++ b/tests/assets/stubs/niswitch_measurement/__init__.py @@ -0,0 +1 @@ +"""Auto generated gRPC files for niswitch test measurement.""" diff --git a/tests/assets/stubs/niswitch_measurement/types.proto b/tests/assets/stubs/niswitch_measurement/types.proto new file mode 100644 index 000000000..7ff340cb0 --- /dev/null +++ b/tests/assets/stubs/niswitch_measurement/types.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; +package ni.measurementlink.measurement.tests.niswitch_measurement; + +message Configurations { + repeated string relay_names = 1; + bool multi_session = 2; +} + +message Outputs { + repeated string session_names = 1; + repeated string resource_names = 2; + repeated string channel_lists = 3; + repeated string connected_channels = 4; +} \ No newline at end of file diff --git a/tests/assets/stubs/niswitch_measurement/types_pb2.py b/tests/assets/stubs/niswitch_measurement/types_pb2.py new file mode 100644 index 000000000..dbe3635b2 --- /dev/null +++ b/tests/assets/stubs/niswitch_measurement/types_pb2.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: stubs/niswitch_measurement/types.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&stubs/niswitch_measurement/types.proto\x12\x39ni.measurementlink.measurement.tests.niswitch_measurement\"<\n\x0e\x43onfigurations\x12\x13\n\x0brelay_names\x18\x01 \x03(\t\x12\x15\n\rmulti_session\x18\x02 \x01(\x08\"k\n\x07Outputs\x12\x15\n\rsession_names\x18\x01 \x03(\t\x12\x16\n\x0eresource_names\x18\x02 \x03(\t\x12\x15\n\rchannel_lists\x18\x03 \x03(\t\x12\x1a\n\x12\x63onnected_channels\x18\x04 \x03(\tb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'stubs.niswitch_measurement.types_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _CONFIGURATIONS._serialized_start=101 + _CONFIGURATIONS._serialized_end=161 + _OUTPUTS._serialized_start=163 + _OUTPUTS._serialized_end=270 +# @@protoc_insertion_point(module_scope) diff --git a/tests/assets/stubs/niswitch_measurement/types_pb2.pyi b/tests/assets/stubs/niswitch_measurement/types_pb2.pyi new file mode 100644 index 000000000..6bdcb40cc --- /dev/null +++ b/tests/assets/stubs/niswitch_measurement/types_pb2.pyi @@ -0,0 +1,64 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class Configurations(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RELAY_NAMES_FIELD_NUMBER: builtins.int + MULTI_SESSION_FIELD_NUMBER: builtins.int + @property + def relay_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + multi_session: builtins.bool + def __init__( + self, + *, + relay_names: collections.abc.Iterable[builtins.str] | None = ..., + multi_session: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["multi_session", b"multi_session", "relay_names", b"relay_names"]) -> None: ... + +global___Configurations = Configurations + +@typing_extensions.final +class Outputs(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SESSION_NAMES_FIELD_NUMBER: builtins.int + RESOURCE_NAMES_FIELD_NUMBER: builtins.int + CHANNEL_LISTS_FIELD_NUMBER: builtins.int + CONNECTED_CHANNELS_FIELD_NUMBER: builtins.int + @property + def session_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + @property + def resource_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + @property + def channel_lists(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + @property + def connected_channels(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + def __init__( + self, + *, + session_names: collections.abc.Iterable[builtins.str] | None = ..., + resource_names: collections.abc.Iterable[builtins.str] | None = ..., + channel_lists: collections.abc.Iterable[builtins.str] | None = ..., + connected_channels: collections.abc.Iterable[builtins.str] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["channel_lists", b"channel_lists", "connected_channels", b"connected_channels", "resource_names", b"resource_names", "session_names", b"session_names"]) -> None: ... + +global___Outputs = Outputs diff --git a/tests/assets/stubs/niswitch_measurement/types_pb2_grpc.py b/tests/assets/stubs/niswitch_measurement/types_pb2_grpc.py new file mode 100644 index 000000000..2daafffeb --- /dev/null +++ b/tests/assets/stubs/niswitch_measurement/types_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/tests/assets/stubs/niswitch_measurement/types_pb2_grpc.pyi b/tests/assets/stubs/niswitch_measurement/types_pb2_grpc.pyi new file mode 100644 index 000000000..b13382f6a --- /dev/null +++ b/tests/assets/stubs/niswitch_measurement/types_pb2_grpc.pyi @@ -0,0 +1,17 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import abc +import collections.abc +import grpc +import grpc.aio +import typing + +_T = typing.TypeVar('_T') + +class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta): + ... + +class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore + ... diff --git a/tests/integration/session_management/test_niswitch_reservation.py b/tests/integration/session_management/test_niswitch_reservation.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/utilities/niswitch_measurement/NISwitchMeasurement.serviceconfig b/tests/utilities/niswitch_measurement/NISwitchMeasurement.serviceconfig new file mode 100644 index 000000000..91193c821 --- /dev/null +++ b/tests/utilities/niswitch_measurement/NISwitchMeasurement.serviceconfig @@ -0,0 +1,19 @@ +{ + "services": [ + { + "displayName": "NI-Switch Measurement (Py)", + "serviceClass": "ni.tests.NISwitchMeasurement_Python", + "descriptionUrl": "", + "providedInterfaces": [ + "ni.measurementlink.measurement.v1.MeasurementService", + "ni.measurementlink.measurement.v2.MeasurementService" + ], + "path": "start.bat", + "annotations": { + "ni/service.description": "NI-Switch MeasurementLink test service.", + "ni/service.collection": "NI.Tests", + "ni/service.tags": [] + } + } + ] +} \ No newline at end of file diff --git a/tests/utilities/niswitch_measurement/__init__.py b/tests/utilities/niswitch_measurement/__init__.py new file mode 100644 index 000000000..2eaf32366 --- /dev/null +++ b/tests/utilities/niswitch_measurement/__init__.py @@ -0,0 +1,67 @@ +"""NI-Switch MeasurementLink test service.""" +import pathlib +from typing import Iterable, Sequence, Tuple + +import niswitch +from niswitch.enums import RelayAction + +import ni_measurementlink_service as nims + +service_directory = pathlib.Path(__file__).resolve().parent +measurement_service = nims.MeasurementService( + service_config_path=service_directory / "NISwitchMeasurement.serviceconfig", + version="0.1.0.0", + ui_file_paths=[ + service_directory, + ], +) + + +@measurement_service.register_measurement +@measurement_service.configuration("relay_names", nims.DataType.StringArray1D, ["SiteRelay1"]) +@measurement_service.configuration("multi_session", nims.DataType.Boolean, False) +@measurement_service.output("session_names", nims.DataType.StringArray1D) +@measurement_service.output("resource_names", nims.DataType.StringArray1D) +@measurement_service.output("channel_lists", nims.DataType.StringArray1D) +@measurement_service.output("connected_channels", nims.DataType.StringArray1D) +def measure( + relay_names: Iterable[str], + multi_session: bool, +) -> Tuple[Iterable[str], Iterable[str], Iterable[str], Iterable[str]]: + """NI-Switch MeasurementLink test service.""" + if multi_session: + with measurement_service.context.reserve_sessions(relay_names) as reservation: + with reservation.initialize_niswitch_sessions() as session_infos: + connections = reservation.get_niswitch_connections(relay_names) + assert all([session_info is not None for session_info in session_infos]) + _control_relays(session_infos) + + return ( + [session_info.session_name for session_info in session_infos], + [session_info.resource_name for session_info in session_infos], + [session_info.channel_list for session_info in session_infos], + [connection.channel_name for connection in connections], + ) + else: + with measurement_service.context.reserve_session(relay_names) as reservation: + with reservation.initialize_niswitch_session() as session_info: + connection = reservation.get_niswitch_connection(list(relay_names)[0]) + assert session_info is not None + _control_relays([session_info]) + + return ( + [session_info.session_name], + [session_info.resource_name], + [session_info.channel_list], + [connection.channel_name], + ) + + +def _control_relays( + session_infos: Sequence[nims.session_management.TypedSessionInformation[niswitch.Session]], +) -> None: + for session_info in session_infos: + session_info.session.relay_control(session_info.channel_list, RelayAction.CLOSE) + + for session_info in session_infos: + session_info.session.wait_for_debounce() From df7914eab9323e5eae3799923221122223c2aa2f Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Fri, 3 Nov 2023 17:18:14 +0530 Subject: [PATCH 03/19] fix: add pins and sites for nidigital pin map --- .../2Digital2Group4Pin1Site.pinmap | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap b/tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap index 21b25bed6..f8f38e9d9 100644 --- a/tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap +++ b/tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap @@ -10,14 +10,26 @@ - + + + + + + + + + - - + + + + + + \ No newline at end of file From bd701c870090c8c74e36b9738ecb43a1dbda2ba0 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Fri, 3 Nov 2023 18:05:43 +0530 Subject: [PATCH 04/19] tests: add assets to integration dir and add niswitch integration tests refactor: niswitch integration tests fix: get niswitch connections test --- .../2Digital2Group4Pin1Site.pinmap | 35 +++++++ .../2Switch2Relay1Site.pinmap | 21 ++++ .../session_management/Pattern.digipat | Bin 0 -> 4188 bytes .../session_management/PinLevels.digilevels | 51 ++++++++++ .../session_management/Specifications.specs | 16 +++ .../session_management/Timing.digitiming | 57 +++++++++++ .../test_niswitch_reservation.py | 94 ++++++++++++++++++ 7 files changed, 274 insertions(+) create mode 100644 tests/assets/integration/session_management/2Digital2Group4Pin1Site.pinmap create mode 100644 tests/assets/integration/session_management/2Switch2Relay1Site.pinmap create mode 100644 tests/assets/integration/session_management/Pattern.digipat create mode 100644 tests/assets/integration/session_management/PinLevels.digilevels create mode 100644 tests/assets/integration/session_management/Specifications.specs create mode 100644 tests/assets/integration/session_management/Timing.digitiming diff --git a/tests/assets/integration/session_management/2Digital2Group4Pin1Site.pinmap b/tests/assets/integration/session_management/2Digital2Group4Pin1Site.pinmap new file mode 100644 index 000000000..f8f38e9d9 --- /dev/null +++ b/tests/assets/integration/session_management/2Digital2Group4Pin1Site.pinmap @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/assets/integration/session_management/2Switch2Relay1Site.pinmap b/tests/assets/integration/session_management/2Switch2Relay1Site.pinmap new file mode 100644 index 000000000..cf9e89756 --- /dev/null +++ b/tests/assets/integration/session_management/2Switch2Relay1Site.pinmap @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/assets/integration/session_management/Pattern.digipat b/tests/assets/integration/session_management/Pattern.digipat new file mode 100644 index 0000000000000000000000000000000000000000..a3566238de9e248a218a8b903a5ef5b5ed0604ce GIT binary patch literal 4188 zcmcgvYi`p(5O$z|cnHc<2oRWG2B{GD-3kO$F^{OPsHuvCgeo`Pwl1+9wj0zFAdbKp z_{~qwz!^9K;sP+AcPHMqiIlVrgQj0R-ktqCvopIa2i5qr<2cb3$Jro+Fc#=HwD^}~ zfPO%eFcvQ1dD3d7>{ddhgiP?bE_P}%YN#kpFamOlR7I+uO@RSxg-x}UN*9kL%NBzn zZE_g*Fu+3_2CIgF?dPHK!+5~Y1sbp-I_eO;4LV8FUj2-d$OaqjXq|dONkLQZg-&ZDwT1lyleCtA;D0V!<7_o!ENJ)5uUNizF73R5bA~nBUqs4 zwPBHErJSsb-7Ff|`NtQHcZSDPgWX00Ck_OhS&$dx3fmOXE>)|gx!L)}YI(j~U9@}a z0=AiF%()HO{Cw4kO6iBc)PpLk6^P)$2p8N<)MZL~!-6 ziM-s!QTIQ>k{C9OVB>aX)5o~f3=>8$F{fKAL3GYlRj5Rk7F@qEh~;Pp%93$YG6>H- zY*r?6iP;dMO6hiE-{HA5c$Md7IHvcw8+k?JVNd;kH!s_q2d!APyY`|-Kz8>#C)+L3 zAyRbv3h5fDBK=JImGm3w57M8czexX(4v?Q4qz^2`N#ZWiMZWk}Qe$-=xj0wcV0b)x zGc%D}#&Vj|pxBK~fiX~)RL$>X^s*ML`0-#WFDuXkFzV+|G zlT#^F?1eYXd`9exo1}KLDkt + + + + + dc.vcc * 0.7 + 0 + 2.5 + 0.5 + 1.5 m + -1.5 m + 0 + 0 + HighZ + + + dc.vcc * 0.7 + 0 + 2.5 + 0.5 + 1.5 m + -1.5 m + 0 + 0 + HighZ + + + dc.vcc * 0.7 + 0 + 2.5 + 0.5 + 1.5 m + -1.5 m + 0 + 0 + HighZ + + + dc.vcc * 0.7 + 0 + 2.5 + 0.5 + 1.5 m + -1.5 m + 0 + 0 + HighZ + + + + \ No newline at end of file diff --git a/tests/assets/integration/session_management/Specifications.specs b/tests/assets/integration/session_management/Specifications.specs new file mode 100644 index 000000000..8792bc803 --- /dev/null +++ b/tests/assets/integration/session_management/Specifications.specs @@ -0,0 +1,16 @@ + + +
+ + 5 + V + +
+
+ + 1 / 1000000 + 1 MHz + +
+ +
\ No newline at end of file diff --git a/tests/assets/integration/session_management/Timing.digitiming b/tests/assets/integration/session_management/Timing.digitiming new file mode 100644 index 000000000..df69bee5d --- /dev/null +++ b/tests/assets/integration/session_management/Timing.digitiming @@ -0,0 +1,57 @@ + + + + + + ac.period + + + + 0 + 0 + ac.period + + + (3 * ac.period) / 4 + + Pattern + + + + ac.period / 2 + ac.period / 2 + ac.period + ac.period + + + (3 * ac.period) / 4 + + Pattern + + + + 0 + 0 + ac.period + + + (3 * ac.period) / 4 + + Pattern + + + + 0 + 0 + ac.period + + + (3 * ac.period) / 4 + + Pattern + + + + + + \ No newline at end of file diff --git a/tests/integration/session_management/test_niswitch_reservation.py b/tests/integration/session_management/test_niswitch_reservation.py index e69de29bb..922d1a848 100644 --- a/tests/integration/session_management/test_niswitch_reservation.py +++ b/tests/integration/session_management/test_niswitch_reservation.py @@ -0,0 +1,94 @@ +import pathlib +from contextlib import ExitStack + +import pytest + +from ni_measurementlink_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 + + +def test___single_session_reserved___initialize_niswitch_session___creates_single_session( + pin_map_context: PinMapContext, + session_management_client: SessionManagementClient, +) -> None: + relay_names = ["SiteRelay1"] + with ExitStack() as stack: + reservation = stack.enter_context( + session_management_client.reserve_session(pin_map_context, relay_names) + ) + + session_info = stack.enter_context(reservation.initialize_niswitch_session()) + + assert session_info.session is not None + assert session_info.session_name == "RelayDriver1" + + +def test___multiple_sessions_reserved___initialize_niswitch_sessions___creates_multiple_sessions( + pin_map_context: PinMapContext, + session_management_client: SessionManagementClient, +) -> None: + relay_names = ["SiteRelay1", "SiteRelay2"] + niswitch_resource = ["RelayDriver1", "RelayDriver2"] + with ExitStack() as stack: + reservation = stack.enter_context( + session_management_client.reserve_sessions(pin_map_context, relay_names) + ) + + session_infos = stack.enter_context(reservation.initialize_niswitch_sessions()) + + assert all([session_info.session is not None for session_info in session_infos]) + assert [ + session_info.session_name == expected_resource + for session_info, expected_resource in zip(session_infos, niswitch_resource) + ] + + +def test___session_created___get_niswitch_connection___returns_connection( + pin_map_context: PinMapContext, + session_management_client: SessionManagementClient, +) -> None: + relay_names = ["SiteRelay1"] + with ExitStack() as stack: + reservation = stack.enter_context( + session_management_client.reserve_session(pin_map_context, relay_names) + ) + stack.enter_context(reservation.initialize_niswitch_session()) + + connection = reservation.get_niswitch_connection(relay_names[0]) + + assert get_connection_subset(connection) == ConnectionSubset( + relay_names[0], _SITE, "RelayDriver1", "K0" + ) + + +def test___sessions_created___get_niswitch_connections___returns_connections( + pin_map_context: PinMapContext, + session_management_client: SessionManagementClient, +) -> None: + relay_names = ["SiteRelay1", "SiteRelay2"] + with ExitStack() as stack: + reservation = stack.enter_context( + session_management_client.reserve_sessions(pin_map_context, relay_names) + ) + stack.enter_context(reservation.initialize_niswitch_sessions()) + + connections = reservation.get_niswitch_connections(relay_names) + + assert [get_connection_subset(connection) for connection in connections] == [ + ConnectionSubset(relay_names[0], _SITE, "RelayDriver1", "K0"), + ConnectionSubset(relay_names[1], _SITE, "RelayDriver2", "K1"), + ] + + +@pytest.fixture +def pin_map_context(pin_map_client: PinMapClient, pin_map_directory: pathlib.Path) -> PinMapContext: + pin_map_name = "2Switch2Relay1Site.pinmap" + pin_map_id = pin_map_client.update_pin_map(pin_map_directory / pin_map_name) + + return PinMapContext(pin_map_id=pin_map_id, sites=[_SITE]) From 9f7008bd649612ed2aa771f1e093b91e54bd20aa Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Fri, 3 Nov 2023 19:39:33 +0530 Subject: [PATCH 05/19] tests: add integration tests for nidigital session APIs tests: add integration tests for nidigital driver session APIs --- .../test_nidigital_reservation.py | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/tests/integration/session_management/test_nidigital_reservation.py b/tests/integration/session_management/test_nidigital_reservation.py index e69de29bb..d2d18671e 100644 --- a/tests/integration/session_management/test_nidigital_reservation.py +++ b/tests/integration/session_management/test_nidigital_reservation.py @@ -0,0 +1,94 @@ +import pathlib +from contextlib import ExitStack + +import pytest + +from ni_measurementlink_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( + pin_map_id: str, + session_management_client: SessionManagementClient, +) -> None: + pin_names = ["CS"] + pin_map_context = PinMapContext(pin_map_id=pin_map_id, sites=[0]) + with ExitStack() as stack: + reservation = stack.enter_context( + session_management_client.reserve_session(pin_map_context, pin_names) + ) + + session_info = stack.enter_context(reservation.initialize_nidigital_session()) + + assert session_info.session is not None + assert session_info.session_name == "DigitalPattern1" + + +def test___multiple_sessions_reserved___initialize_nidigital_sessions___creates_multiple_sessions( + pin_map_id: str, + session_management_client: SessionManagementClient, +) -> None: + pin_names = ["CS", "SCLK"] + nidigital_resource = ["DigitalPattern1", "DigitalPattern2"] + pin_map_context = PinMapContext(pin_map_id=pin_map_id, sites=[0, 1]) + with ExitStack() as stack: + reservation = stack.enter_context( + session_management_client.reserve_sessions(pin_map_context, pin_names) + ) + + session_infos = stack.enter_context(reservation.initialize_nidigital_sessions()) + + assert all([session_info.session is not None for session_info in session_infos]) + assert [ + session_info.session_name == expected_resouce + for session_info, expected_resouce in zip(session_infos, nidigital_resource) + ] + + +def test___session_created___get_nidigital_connection___returns_connection( + pin_map_id: str, + session_management_client: SessionManagementClient, +) -> None: + pin_names = ["CS"] + pin_map_context = PinMapContext(pin_map_id=pin_map_id, sites=[0]) + with ExitStack() as stack: + reservation = stack.enter_context( + session_management_client.reserve_session(pin_map_context, pin_names) + ) + stack.enter_context(reservation.initialize_nidigital_session()) + + connection = reservation.get_nidigital_connection(pin_names[0]) + + assert get_connection_subset(connection) == ConnectionSubset( + pin_names[0], 0, "DigitalPattern1", "site0/CS" + ) + + +def test___session_created___get_nidigital_connections_returns_connections( + pin_map_id: str, + session_management_client: SessionManagementClient, +) -> None: + pin_names = ["CS", "SCLK"] + pin_map_context = PinMapContext(pin_map_id=pin_map_id, sites=[0]) + with ExitStack() as stack: + reservation = stack.enter_context( + session_management_client.reserve_session(pin_map_context, pin_names) + ) + stack.enter_context(reservation.initialize_nidigital_session()) + + connections = reservation.get_nidigital_connections(pin_names) + + assert [get_connection_subset(connection) for connection in connections] == [ + ConnectionSubset(pin_names[0], 0, "DigitalPattern1", "site0/CS"), + ConnectionSubset(pin_names[1], 0, "DigitalPattern1", "site0/SCLK"), + ] + + +@pytest.fixture +def pin_map_id(pin_map_client: PinMapClient, pin_map_directory: pathlib.Path) -> str: + pin_map_name = "2Digital2Group4Pin1Site.pinmap" + return pin_map_client.update_pin_map(pin_map_directory / pin_map_name) From bcba397dd100af4f7ea2af58c9739fe9615c19fe Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Mon, 6 Nov 2023 11:22:02 +0530 Subject: [PATCH 06/19] tests: add acceptance tests for niswitch driver session APIs --- tests/acceptance/test_niswitch_measurement.py | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/tests/acceptance/test_niswitch_measurement.py b/tests/acceptance/test_niswitch_measurement.py index e69de29bb..2b156ddcf 100644 --- a/tests/acceptance/test_niswitch_measurement.py +++ b/tests/acceptance/test_niswitch_measurement.py @@ -0,0 +1,96 @@ +import pathlib +from typing import Generator, Iterable, NamedTuple + +import pytest + +from ni_measurementlink_service._internal.stubs.ni.measurementlink.measurement.v2.measurement_service_pb2 import ( + MeasureRequest, +) +from ni_measurementlink_service._internal.stubs.ni.measurementlink.measurement.v2.measurement_service_pb2_grpc import ( + MeasurementServiceStub, +) +from ni_measurementlink_service._internal.stubs.ni.measurementlink.pin_map_context_pb2 import ( + PinMapContext, +) +from ni_measurementlink_service.measurement.service import MeasurementService +from tests.assets.stubs.niswitch_measurement.types_pb2 import ( + Configurations, + Outputs, +) +from tests.utilities import niswitch_measurement +from tests.utilities.pin_map_client import PinMapClient + +_SITE = 0 + + +def test___single_session___measure___creates_single_session( + pin_map_context: PinMapContext, + stub_v2: MeasurementServiceStub, +) -> None: + configurations = Configurations(relay_names=["SiteRelay1"], multi_session=False) + + outputs = _measure(stub_v2, pin_map_context, configurations) + + assert _get_output(outputs) == [_MeasurementOutput("RelayDriver1", "RelayDriver1", "K0", "K0")] + + +def test___multiple_sessions___measure___creates_multiple_sessions( + pin_map_context: PinMapContext, + stub_v2: MeasurementServiceStub, +) -> None: + configurations = Configurations(relay_names=["SiteRelay1", "SiteRelay2"], multi_session=True) + + outputs = _measure(stub_v2, pin_map_context, configurations) + + assert _get_output(outputs) == [ + _MeasurementOutput("RelayDriver1", "RelayDriver1", "K0", "K0"), + _MeasurementOutput("RelayDriver2", "RelayDriver2", "K1", "K1"), + ] + + +def _measure( + stub_v2: MeasurementServiceStub, + pin_map_context: PinMapContext, + configurations: Configurations, +) -> Outputs: + request = MeasureRequest(pin_map_context=pin_map_context) + request.configuration_parameters.Pack(configurations) + response_iterator = stub_v2.Measure(request) + responses = list(response_iterator) + assert len(responses) == 1 + outputs = Outputs.FromString(responses[0].outputs.value) + return outputs + + +@pytest.fixture(scope="module") +def measurement_service() -> Generator[MeasurementService, None, None]: + """Test fixture that creates and hosts a measurement service.""" + with niswitch_measurement.measurement_service.host_service() as service: + yield service + + +@pytest.fixture +def pin_map_context(pin_map_client: PinMapClient, pin_map_directory: pathlib.Path) -> PinMapContext: + pin_map_name = "2Switch2Relay1Site.pinmap" + pin_map_id = pin_map_client.update_pin_map(pin_map_directory / pin_map_name) + + return PinMapContext(pin_map_id=pin_map_id, sites=[_SITE]) + + +class _MeasurementOutput(NamedTuple): + session_name: str + resource_name: str + channel_list: str + connected_channels: str + + +def _get_output(outputs: Outputs) -> Iterable[_MeasurementOutput]: + return [ + _MeasurementOutput(session_name, resource_name, channel_list, connected_channels) + for session_name, resource_name, channel_list, connected_channels in zip( + outputs.session_names, + outputs.resource_names, + outputs.channel_lists, + outputs.connected_channels, + ) + ] From 4347e382d8358faf7433f5c5096024825965e6d9 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Mon, 6 Nov 2023 11:23:53 +0530 Subject: [PATCH 07/19] tests: add acceptance tests for nidigital driver session APIs --- .../acceptance/test_nidigital_measurement.py | 121 ++++++++++++++++++ .../2Digital2Group4Pin1Site.pinmap | 9 +- .../stubs/nidigital_measurement/types.proto | 2 +- .../stubs/nidigital_measurement/types_pb2.py | 8 +- .../stubs/nidigital_measurement/types_pb2.pyi | 8 +- .../nidigital_measurement/__init__.py | 12 +- 6 files changed, 137 insertions(+), 23 deletions(-) diff --git a/tests/acceptance/test_nidigital_measurement.py b/tests/acceptance/test_nidigital_measurement.py index e69de29bb..88ad2c049 100644 --- a/tests/acceptance/test_nidigital_measurement.py +++ b/tests/acceptance/test_nidigital_measurement.py @@ -0,0 +1,121 @@ +import pathlib +from typing import Generator, Iterable, NamedTuple + +import pytest + +from ni_measurementlink_service._internal.stubs.ni.measurementlink.measurement.v2.measurement_service_pb2 import ( + MeasureRequest, +) +from ni_measurementlink_service._internal.stubs.ni.measurementlink.measurement.v2.measurement_service_pb2_grpc import ( + MeasurementServiceStub, +) +from ni_measurementlink_service._internal.stubs.ni.measurementlink.pin_map_context_pb2 import ( + PinMapContext, +) +from ni_measurementlink_service.measurement.service import MeasurementService +from tests.assets.stubs.nidigital_measurement.types_pb2 import Configurations, Outputs +from tests.utilities import nidigital_measurement +from tests.utilities.pin_map_client import PinMapClient + + +def test___single_session___measure___returns_measured_values( + pin_map_id: str, + stub_v2: MeasurementServiceStub, +) -> None: + pin_map_context = PinMapContext(pin_map_id=pin_map_id, sites=[0]) + configurations = Configurations(pin_names=["CS", "SCLK", "MOSI", "MISO"], multi_session=False) + + outputs = _measure(stub_v2, pin_map_context, configurations) + + assert outputs.passing_sites == [0] + assert outputs.failing_sites == [] + + +def test___single_session___measure___creates_single_session( + pin_map_id: str, + stub_v2: MeasurementServiceStub, +) -> None: + pin_map_context = PinMapContext(pin_map_id=pin_map_id, sites=[0]) + configurations = Configurations(pin_names=["CS", "SCLK", "MOSI", "MISO"], multi_session=False) + + outputs = _measure(stub_v2, pin_map_context, configurations) + + assert _get_output(outputs) == [ + _MeasurementOutput( + "DigitalPattern1", + "DigitalPattern1", + "site0/CS, site0/SCLK, site0/MOSI, site0/MISO", + "site0/CS", + ) + ] + + +def test___multiple_session___measure___creates_multiple_sessions( + pin_map_id: str, + stub_v2: MeasurementServiceStub, +) -> None: + pin_map_context = PinMapContext(pin_map_id=pin_map_id, sites=[0, 1]) + configurations = Configurations(pin_names=["CS"], multi_session=True) + + outputs = _measure(stub_v2, pin_map_context, configurations) + + assert _get_output(outputs) == [ + _MeasurementOutput( + "DigitalPattern1", + "DigitalPattern1", + "site0/CS", + "site0/CS", + ), + _MeasurementOutput( + "DigitalPattern2", + "DigitalPattern2", + "site1/CS", + "site1/CS", + ), + ] + + +def _measure( + stub_v2: MeasurementServiceStub, + pin_map_context: PinMapContext, + configurations: Configurations, +) -> Outputs: + request = MeasureRequest(pin_map_context=pin_map_context) + request.configuration_parameters.Pack(configurations) + response_iterator = stub_v2.Measure(request) + responses = list(response_iterator) + assert len(responses) == 1 + outputs = Outputs.FromString(responses[0].outputs.value) + return outputs + + +@pytest.fixture(scope="module") +def measurement_service() -> Generator[MeasurementService, None, None]: + """Test fixture that creates and hosts a measurement service.""" + with nidigital_measurement.measurement_service.host_service() as service: + yield service + + +@pytest.fixture +def pin_map_id(pin_map_client: PinMapClient, pin_map_directory: pathlib.Path) -> str: + pin_map_name = "2Digital2Group4Pin1Site.pinmap" + return pin_map_client.update_pin_map(pin_map_directory / pin_map_name) + + +class _MeasurementOutput(NamedTuple): + session_name: str + resource_name: str + channel_list: str + connected_channels: str + + +def _get_output(outputs: Outputs) -> Iterable[_MeasurementOutput]: + return [ + _MeasurementOutput(session_name, resource_name, channel_list, connected_channels) + for session_name, resource_name, channel_list, connected_channels in zip( + outputs.session_names, + outputs.resource_names, + outputs.channel_lists, + outputs.connected_channels, + ) + ] diff --git a/tests/assets/integration/session_management/2Digital2Group4Pin1Site.pinmap b/tests/assets/integration/session_management/2Digital2Group4Pin1Site.pinmap index f8f38e9d9..1054c1965 100644 --- a/tests/assets/integration/session_management/2Digital2Group4Pin1Site.pinmap +++ b/tests/assets/integration/session_management/2Digital2Group4Pin1Site.pinmap @@ -10,14 +10,7 @@ - - - - - - - - + diff --git a/tests/assets/stubs/nidigital_measurement/types.proto b/tests/assets/stubs/nidigital_measurement/types.proto index cec3c0e59..1ce30e29a 100644 --- a/tests/assets/stubs/nidigital_measurement/types.proto +++ b/tests/assets/stubs/nidigital_measurement/types.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package ni.measurementlink.measurement.tests.nidigital_measurement; message Configurations { - repeated string relay_names = 1; + repeated string pin_names = 1; bool multi_session = 2; } diff --git a/tests/assets/stubs/nidigital_measurement/types_pb2.py b/tests/assets/stubs/nidigital_measurement/types_pb2.py index e2633ded6..177fcdc73 100644 --- a/tests/assets/stubs/nidigital_measurement/types_pb2.py +++ b/tests/assets/stubs/nidigital_measurement/types_pb2.py @@ -13,7 +13,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'stubs/nidigital_measurement/types.proto\x12:ni.measurementlink.measurement.tests.nidigital_measurement\"<\n\x0e\x43onfigurations\x12\x13\n\x0brelay_names\x18\x01 \x03(\t\x12\x15\n\rmulti_session\x18\x02 \x01(\x08\"\x99\x01\n\x07Outputs\x12\x15\n\rsession_names\x18\x01 \x03(\t\x12\x16\n\x0eresource_names\x18\x02 \x03(\t\x12\x15\n\rchannel_lists\x18\x03 \x03(\t\x12\x1a\n\x12\x63onnected_channels\x18\x04 \x03(\t\x12\x15\n\rpassing_sites\x18\x05 \x03(\x05\x12\x15\n\rfailing_sites\x18\x06 \x03(\x05\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'stubs/nidigital_measurement/types.proto\x12:ni.measurementlink.measurement.tests.nidigital_measurement\":\n\x0e\x43onfigurations\x12\x11\n\tpin_names\x18\x01 \x03(\t\x12\x15\n\rmulti_session\x18\x02 \x01(\x08\"\x99\x01\n\x07Outputs\x12\x15\n\rsession_names\x18\x01 \x03(\t\x12\x16\n\x0eresource_names\x18\x02 \x03(\t\x12\x15\n\rchannel_lists\x18\x03 \x03(\t\x12\x1a\n\x12\x63onnected_channels\x18\x04 \x03(\t\x12\x15\n\rpassing_sites\x18\x05 \x03(\x05\x12\x15\n\rfailing_sites\x18\x06 \x03(\x05\x62\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'stubs.nidigital_measurement.types_pb2', globals()) @@ -21,7 +21,7 @@ DESCRIPTOR._options = None _CONFIGURATIONS._serialized_start=103 - _CONFIGURATIONS._serialized_end=163 - _OUTPUTS._serialized_start=166 - _OUTPUTS._serialized_end=319 + _CONFIGURATIONS._serialized_end=161 + _OUTPUTS._serialized_start=164 + _OUTPUTS._serialized_end=317 # @@protoc_insertion_point(module_scope) diff --git a/tests/assets/stubs/nidigital_measurement/types_pb2.pyi b/tests/assets/stubs/nidigital_measurement/types_pb2.pyi index aaf224b5f..c448f2b36 100644 --- a/tests/assets/stubs/nidigital_measurement/types_pb2.pyi +++ b/tests/assets/stubs/nidigital_measurement/types_pb2.pyi @@ -20,18 +20,18 @@ DESCRIPTOR: google.protobuf.descriptor.FileDescriptor class Configurations(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - RELAY_NAMES_FIELD_NUMBER: builtins.int + PIN_NAMES_FIELD_NUMBER: builtins.int MULTI_SESSION_FIELD_NUMBER: builtins.int @property - def relay_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + def pin_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... multi_session: builtins.bool def __init__( self, *, - relay_names: collections.abc.Iterable[builtins.str] | None = ..., + pin_names: collections.abc.Iterable[builtins.str] | None = ..., multi_session: builtins.bool = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["multi_session", b"multi_session", "relay_names", b"relay_names"]) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["multi_session", b"multi_session", "pin_names", b"pin_names"]) -> None: ... global___Configurations = Configurations diff --git a/tests/utilities/nidigital_measurement/__init__.py b/tests/utilities/nidigital_measurement/__init__.py index 81b4da227..d5fc64935 100644 --- a/tests/utilities/nidigital_measurement/__init__.py +++ b/tests/utilities/nidigital_measurement/__init__.py @@ -17,7 +17,7 @@ @measurement_service.register_measurement -@measurement_service.configuration("pin_names", nims.DataType.StringArray1D, ["SPI_PINS"]) +@measurement_service.configuration("pin_names", nims.DataType.StringArray1D, ["CS"]) @measurement_service.configuration("multi_session", nims.DataType.Boolean, False) @measurement_service.output("session_names", nims.DataType.StringArray1D) @measurement_service.output("resource_names", nims.DataType.StringArray1D) @@ -44,8 +44,8 @@ def measure( [session_info.resource_name for session_info in session_infos], [session_info.channel_list for session_info in session_infos], [connection.channel_name for connection in connections], - passing_sites, - failing_sites, + list(passing_sites), + list(failing_sites), ) else: with measurement_service.context.reserve_session(pin_names) as reservation: @@ -87,7 +87,7 @@ def _spi( str(_resolve_relative_path(test_assets_directory, timing_file_path)), ) session.load_pattern( - str(_resolve_relative_path(service_directory, pattern_file_path)), + str(_resolve_relative_path(test_assets_directory, pattern_file_path)), ) levels_file_name = pathlib.Path(levels_file_path).stem @@ -97,8 +97,8 @@ def _spi( site_pass_fail = selected_sites.get_site_pass_fail() passing_sites = [site for site, pass_fail in site_pass_fail.items() if pass_fail] failing_sites = [site for site, pass_fail in site_pass_fail.items() if not pass_fail] - passing_sites_list.append(passing_sites) - failing_sites_list.append(failing_sites) + passing_sites_list.extend(passing_sites) + failing_sites_list.extend(failing_sites) session.selected_function = nidigital.SelectedFunction.DISCONNECT return (passing_sites_list, failing_sites_list) From 6b896c32cba3d17be5264439c9e7f9f1bd786301 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Mon, 6 Nov 2023 11:32:56 +0530 Subject: [PATCH 08/19] fix: lint errors --- tests/acceptance/test_nidigital_measurement.py | 2 +- tests/utilities/niswitch_measurement/__init__.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/acceptance/test_nidigital_measurement.py b/tests/acceptance/test_nidigital_measurement.py index 88ad2c049..4a85cb240 100644 --- a/tests/acceptance/test_nidigital_measurement.py +++ b/tests/acceptance/test_nidigital_measurement.py @@ -52,7 +52,7 @@ def test___single_session___measure___creates_single_session( def test___multiple_session___measure___creates_multiple_sessions( pin_map_id: str, - stub_v2: MeasurementServiceStub, + stub_v2: MeasurementServiceStub, ) -> None: pin_map_context = PinMapContext(pin_map_id=pin_map_id, sites=[0, 1]) configurations = Configurations(pin_names=["CS"], multi_session=True) diff --git a/tests/utilities/niswitch_measurement/__init__.py b/tests/utilities/niswitch_measurement/__init__.py index 2eaf32366..388ed795e 100644 --- a/tests/utilities/niswitch_measurement/__init__.py +++ b/tests/utilities/niswitch_measurement/__init__.py @@ -3,7 +3,6 @@ from typing import Iterable, Sequence, Tuple import niswitch -from niswitch.enums import RelayAction import ni_measurementlink_service as nims @@ -61,7 +60,7 @@ def _control_relays( session_infos: Sequence[nims.session_management.TypedSessionInformation[niswitch.Session]], ) -> None: for session_info in session_infos: - session_info.session.relay_control(session_info.channel_list, RelayAction.CLOSE) + session_info.session.relay_control(session_info.channel_list, niswitch.enums.RelayAction.CLOSE) for session_info in session_infos: session_info.session.wait_for_debounce() From f6a5685b17ce977c0845a9ccfd4418fcfc396a4c Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Mon, 6 Nov 2023 12:02:51 +0530 Subject: [PATCH 09/19] fix: typo and discard unused attributes in pin map --- tests/acceptance/test_nidigital_measurement.py | 2 +- .../session_management/2Digital2Group4Pin1Site.pinmap | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/acceptance/test_nidigital_measurement.py b/tests/acceptance/test_nidigital_measurement.py index 4a85cb240..b3ba6153b 100644 --- a/tests/acceptance/test_nidigital_measurement.py +++ b/tests/acceptance/test_nidigital_measurement.py @@ -50,7 +50,7 @@ def test___single_session___measure___creates_single_session( ] -def test___multiple_session___measure___creates_multiple_sessions( +def test___multiple_sessions___measure___creates_multiple_sessions( pin_map_id: str, stub_v2: MeasurementServiceStub, ) -> None: diff --git a/tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap b/tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap index f8f38e9d9..1054c1965 100644 --- a/tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap +++ b/tests/assets/acceptance/session_management/2Digital2Group4Pin1Site.pinmap @@ -10,14 +10,7 @@ - - - - - - - - + From 0d90acfebfa09e97dc78b319cb3b409aea9b83f0 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Mon, 6 Nov 2023 12:37:10 +0530 Subject: [PATCH 10/19] fix: lint errors --- tests/utilities/niswitch_measurement/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/utilities/niswitch_measurement/__init__.py b/tests/utilities/niswitch_measurement/__init__.py index 388ed795e..7a85a5de4 100644 --- a/tests/utilities/niswitch_measurement/__init__.py +++ b/tests/utilities/niswitch_measurement/__init__.py @@ -60,7 +60,9 @@ def _control_relays( session_infos: Sequence[nims.session_management.TypedSessionInformation[niswitch.Session]], ) -> None: for session_info in session_infos: - session_info.session.relay_control(session_info.channel_list, niswitch.enums.RelayAction.CLOSE) + session_info.session.relay_control( + session_info.channel_list, niswitch.enums.RelayAction.CLOSE + ) for session_info in session_infos: session_info.session.wait_for_debounce() From 1644c72c18d5541871f861fdfae445790e39537d Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Mon, 6 Nov 2023 15:22:55 +0530 Subject: [PATCH 11/19] refactor: nidigital measurement private api name --- tests/utilities/nidigital_measurement/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/utilities/nidigital_measurement/__init__.py b/tests/utilities/nidigital_measurement/__init__.py index d5fc64935..12522ad07 100644 --- a/tests/utilities/nidigital_measurement/__init__.py +++ b/tests/utilities/nidigital_measurement/__init__.py @@ -37,7 +37,7 @@ def measure( with reservation.initialize_nidigital_sessions() as session_infos: connections = reservation.get_nidigital_connections(pin_names) assert all([session_info is not None for session_info in session_infos]) - passing_sites, failing_sites = _spi(session_infos) + passing_sites, failing_sites = _burst_spi_pattern(session_infos) return ( [session_info.session_name for session_info in session_infos], @@ -52,7 +52,7 @@ def measure( with reservation.initialize_nidigital_session() as session_info: connection = reservation.get_nidigital_connection(list(pin_names)[0]) assert session_info is not None - passing_sites, failing_sites = _spi([session_info]) + passing_sites, failing_sites = _burst_spi_pattern([session_info]) return ( [session_info.session_name], @@ -64,7 +64,7 @@ def measure( ) -def _spi( +def _burst_spi_pattern( session_infos: Sequence[nims.session_management.TypedSessionInformation[nidigital.Session]], ) -> Tuple: test_assets_directory = ( From d2ae52bac6231c9b16b8dff4d5596cff8ec45af6 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Mon, 6 Nov 2023 18:42:51 +0530 Subject: [PATCH 12/19] fix: add underscores between when and then clauses --- .../session_management/test_nidigital_reservation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/session_management/test_nidigital_reservation.py b/tests/integration/session_management/test_nidigital_reservation.py index d2d18671e..fd708bad9 100644 --- a/tests/integration/session_management/test_nidigital_reservation.py +++ b/tests/integration/session_management/test_nidigital_reservation.py @@ -68,7 +68,7 @@ def test___session_created___get_nidigital_connection___returns_connection( ) -def test___session_created___get_nidigital_connections_returns_connections( +def test___session_created___get_nidigital_connections___returns_connections( pin_map_id: str, session_management_client: SessionManagementClient, ) -> None: From 4e80979acac8541cb1dc6fb7a8010901803bd4c5 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Tue, 7 Nov 2023 16:26:54 +0530 Subject: [PATCH 13/19] fix: remove unused files --- .../session_management/Pattern.digipat | Bin 4188 -> 0 bytes .../session_management/PinLevels.digilevels | 51 ---------------- .../session_management/Specifications.specs | 16 ----- .../session_management/Timing.digitiming | 57 ------------------ 4 files changed, 124 deletions(-) delete mode 100644 tests/assets/integration/session_management/Pattern.digipat delete mode 100644 tests/assets/integration/session_management/PinLevels.digilevels delete mode 100644 tests/assets/integration/session_management/Specifications.specs delete mode 100644 tests/assets/integration/session_management/Timing.digitiming diff --git a/tests/assets/integration/session_management/Pattern.digipat b/tests/assets/integration/session_management/Pattern.digipat deleted file mode 100644 index a3566238de9e248a218a8b903a5ef5b5ed0604ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4188 zcmcgvYi`p(5O$z|cnHc<2oRWG2B{GD-3kO$F^{OPsHuvCgeo`Pwl1+9wj0zFAdbKp z_{~qwz!^9K;sP+AcPHMqiIlVrgQj0R-ktqCvopIa2i5qr<2cb3$Jro+Fc#=HwD^}~ zfPO%eFcvQ1dD3d7>{ddhgiP?bE_P}%YN#kpFamOlR7I+uO@RSxg-x}UN*9kL%NBzn zZE_g*Fu+3_2CIgF?dPHK!+5~Y1sbp-I_eO;4LV8FUj2-d$OaqjXq|dONkLQZg-&ZDwT1lyleCtA;D0V!<7_o!ENJ)5uUNizF73R5bA~nBUqs4 zwPBHErJSsb-7Ff|`NtQHcZSDPgWX00Ck_OhS&$dx3fmOXE>)|gx!L)}YI(j~U9@}a z0=AiF%()HO{Cw4kO6iBc)PpLk6^P)$2p8N<)MZL~!-6 ziM-s!QTIQ>k{C9OVB>aX)5o~f3=>8$F{fKAL3GYlRj5Rk7F@qEh~;Pp%93$YG6>H- zY*r?6iP;dMO6hiE-{HA5c$Md7IHvcw8+k?JVNd;kH!s_q2d!APyY`|-Kz8>#C)+L3 zAyRbv3h5fDBK=JImGm3w57M8czexX(4v?Q4qz^2`N#ZWiMZWk}Qe$-=xj0wcV0b)x zGc%D}#&Vj|pxBK~fiX~)RL$>X^s*ML`0-#WFDuXkFzV+|G zlT#^F?1eYXd`9exo1}KLDkt - - - - - dc.vcc * 0.7 - 0 - 2.5 - 0.5 - 1.5 m - -1.5 m - 0 - 0 - HighZ - - - dc.vcc * 0.7 - 0 - 2.5 - 0.5 - 1.5 m - -1.5 m - 0 - 0 - HighZ - - - dc.vcc * 0.7 - 0 - 2.5 - 0.5 - 1.5 m - -1.5 m - 0 - 0 - HighZ - - - dc.vcc * 0.7 - 0 - 2.5 - 0.5 - 1.5 m - -1.5 m - 0 - 0 - HighZ - - - - \ No newline at end of file diff --git a/tests/assets/integration/session_management/Specifications.specs b/tests/assets/integration/session_management/Specifications.specs deleted file mode 100644 index 8792bc803..000000000 --- a/tests/assets/integration/session_management/Specifications.specs +++ /dev/null @@ -1,16 +0,0 @@ - - -
- - 5 - V - -
-
- - 1 / 1000000 - 1 MHz - -
- -
\ No newline at end of file diff --git a/tests/assets/integration/session_management/Timing.digitiming b/tests/assets/integration/session_management/Timing.digitiming deleted file mode 100644 index df69bee5d..000000000 --- a/tests/assets/integration/session_management/Timing.digitiming +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - ac.period - - - - 0 - 0 - ac.period - - - (3 * ac.period) / 4 - - Pattern - - - - ac.period / 2 - ac.period / 2 - ac.period - ac.period - - - (3 * ac.period) / 4 - - Pattern - - - - 0 - 0 - ac.period - - - (3 * ac.period) / 4 - - Pattern - - - - 0 - 0 - ac.period - - - (3 * ac.period) / 4 - - Pattern - - - - - - \ No newline at end of file From 5b4a0b25867e977e75a88c44741f8721a6b28b36 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Tue, 7 Nov 2023 16:27:21 +0530 Subject: [PATCH 14/19] fix: move digital pattern files to test measurement dir --- .../nidigital_measurement}/Pattern.digipat | Bin .../PinLevels.digilevels | 0 .../Specifications.specs | 0 .../nidigital_measurement}/Timing.digitiming | 0 .../nidigital_measurement/__init__.py | 57 ++++++++++++------ 5 files changed, 38 insertions(+), 19 deletions(-) rename tests/{assets/acceptance/session_management => utilities/nidigital_measurement}/Pattern.digipat (100%) rename tests/{assets/acceptance/session_management => utilities/nidigital_measurement}/PinLevels.digilevels (100%) rename tests/{assets/acceptance/session_management => utilities/nidigital_measurement}/Specifications.specs (100%) rename tests/{assets/acceptance/session_management => utilities/nidigital_measurement}/Timing.digitiming (100%) diff --git a/tests/assets/acceptance/session_management/Pattern.digipat b/tests/utilities/nidigital_measurement/Pattern.digipat similarity index 100% rename from tests/assets/acceptance/session_management/Pattern.digipat rename to tests/utilities/nidigital_measurement/Pattern.digipat diff --git a/tests/assets/acceptance/session_management/PinLevels.digilevels b/tests/utilities/nidigital_measurement/PinLevels.digilevels similarity index 100% rename from tests/assets/acceptance/session_management/PinLevels.digilevels rename to tests/utilities/nidigital_measurement/PinLevels.digilevels diff --git a/tests/assets/acceptance/session_management/Specifications.specs b/tests/utilities/nidigital_measurement/Specifications.specs similarity index 100% rename from tests/assets/acceptance/session_management/Specifications.specs rename to tests/utilities/nidigital_measurement/Specifications.specs diff --git a/tests/assets/acceptance/session_management/Timing.digitiming b/tests/utilities/nidigital_measurement/Timing.digitiming similarity index 100% rename from tests/assets/acceptance/session_management/Timing.digitiming rename to tests/utilities/nidigital_measurement/Timing.digitiming diff --git a/tests/utilities/nidigital_measurement/__init__.py b/tests/utilities/nidigital_measurement/__init__.py index 12522ad07..7d927a135 100644 --- a/tests/utilities/nidigital_measurement/__init__.py +++ b/tests/utilities/nidigital_measurement/__init__.py @@ -5,6 +5,7 @@ import nidigital import ni_measurementlink_service as nims +from ni_measurementlink_service.session_management import TypedSessionInformation service_directory = pathlib.Path(__file__).resolve().parent measurement_service = nims.MeasurementService( @@ -38,12 +39,20 @@ def measure( connections = reservation.get_nidigital_connections(pin_names) assert all([session_info is not None for session_info in session_infos]) passing_sites, failing_sites = _burst_spi_pattern(session_infos) + site0_connections, site1_connections = [], [] + # Assumption: All pins connected to site0 belongs to one session and + # all the pins connected to site1 belongs to the other session. + for connection in connections: + if connection.channel_name.startswith("site0"): + site0_connections.append(connection.channel_name) + elif connection.channel_name.startswith("site1"): + site1_connections.append(connection.channel_name) return ( [session_info.session_name for session_info in session_infos], [session_info.resource_name for session_info in session_infos], [session_info.channel_list for session_info in session_infos], - [connection.channel_name for connection in connections], + [", ".join(site0_connections), ", ".join(site1_connections)], list(passing_sites), list(failing_sites), ) @@ -65,35 +74,45 @@ def measure( def _burst_spi_pattern( - session_infos: Sequence[nims.session_management.TypedSessionInformation[nidigital.Session]], + session_infos: Sequence[TypedSessionInformation[nidigital.Session]], ) -> Tuple: - test_assets_directory = ( - service_directory.parent.parent / "assets" / "acceptance" / "session_management" - ) specifications_file_path = "Specifications.specs" levels_file_path = "PinLevels.digilevels" timing_file_path = "Timing.digitiming" pattern_file_path = "Pattern.digipat" pin_map_context = measurement_service.context.pin_map_context + selected_sites_string = ",".join(f"site{i}" for i in pin_map_context.sites or []) + passing_sites_list, failing_sites_list = [], [] for session_info in session_infos: session = session_info.session - selected_sites_string = ",".join(f"site{i}" for i in pin_map_context.sites or []) selected_sites = session.sites[selected_sites_string] - session.load_pin_map(pin_map_context.pin_map_id) - session.load_specifications_levels_and_timing( - str(_resolve_relative_path(test_assets_directory, specifications_file_path)), - str(_resolve_relative_path(test_assets_directory, levels_file_path)), - str(_resolve_relative_path(test_assets_directory, timing_file_path)), - ) - session.load_pattern( - str(_resolve_relative_path(test_assets_directory, pattern_file_path)), - ) - - levels_file_name = pathlib.Path(levels_file_path).stem - timing_file_name = pathlib.Path(timing_file_path).stem + + if not session_info.session_exists: + session.load_pin_map(pin_map_context.pin_map_id) + session.load_specifications_levels_and_timing( + str(_resolve_relative_path(service_directory, specifications_file_path)), + str(_resolve_relative_path(service_directory, levels_file_path)), + str(_resolve_relative_path(service_directory, timing_file_path)), + ) + session.load_pattern( + str(_resolve_relative_path(service_directory, pattern_file_path)), + ) + + levels_file_name = pathlib.Path(levels_file_path).stem + timing_file_name = pathlib.Path(timing_file_path).stem + + for session_info in session_infos: + selected_sites = session_info.session.sites[selected_sites_string] selected_sites.apply_levels_and_timing(levels_file_name, timing_file_name) - selected_sites.burst_pattern(start_label="SPI_Pattern") + + for session_info in session_infos: + selected_sites = session_info.session.sites[selected_sites_string] + selected_sites.burst_pattern(start_label="SPI_Pattern", wait_until_done=False) + + for session_info in session_infos: + selected_sites = session_info.session.sites[selected_sites_string] + session_info.session.wait_until_done() site_pass_fail = selected_sites.get_site_pass_fail() passing_sites = [site for site, pass_fail in site_pass_fail.items() if pass_fail] failing_sites = [site for site, pass_fail in site_pass_fail.items() if not pass_fail] From 7b7abee2a5809a0d34d921efec02ecb771b5e000 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Tue, 7 Nov 2023 16:28:56 +0530 Subject: [PATCH 15/19] tests: pass all the pins to the test measurement --- tests/acceptance/test_nidigital_measurement.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/acceptance/test_nidigital_measurement.py b/tests/acceptance/test_nidigital_measurement.py index b3ba6153b..6af946418 100644 --- a/tests/acceptance/test_nidigital_measurement.py +++ b/tests/acceptance/test_nidigital_measurement.py @@ -55,7 +55,7 @@ def test___multiple_sessions___measure___creates_multiple_sessions( stub_v2: MeasurementServiceStub, ) -> None: pin_map_context = PinMapContext(pin_map_id=pin_map_id, sites=[0, 1]) - configurations = Configurations(pin_names=["CS"], multi_session=True) + configurations = Configurations(pin_names=["CS", "SCLK", "MOSI", "MISO"], multi_session=True) outputs = _measure(stub_v2, pin_map_context, configurations) @@ -63,14 +63,14 @@ def test___multiple_sessions___measure___creates_multiple_sessions( _MeasurementOutput( "DigitalPattern1", "DigitalPattern1", - "site0/CS", - "site0/CS", + "site0/CS, site0/SCLK, site0/MOSI, site0/MISO", + "site0/CS, site0/SCLK, site0/MOSI, site0/MISO", ), _MeasurementOutput( "DigitalPattern2", "DigitalPattern2", - "site1/CS", - "site1/CS", + "site1/CS, site1/SCLK, site1/MOSI, site1/MISO", + "site1/CS, site1/SCLK, site1/MOSI, site1/MISO", ), ] From a982e1ef07c7d78810f020b901e4cad45e7ccf42 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Tue, 7 Nov 2023 17:23:26 +0530 Subject: [PATCH 16/19] style: import nims namespace for TypedSession --- tests/utilities/niswitch_measurement/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/utilities/niswitch_measurement/__init__.py b/tests/utilities/niswitch_measurement/__init__.py index 7a85a5de4..593eb4fb0 100644 --- a/tests/utilities/niswitch_measurement/__init__.py +++ b/tests/utilities/niswitch_measurement/__init__.py @@ -5,6 +5,7 @@ import niswitch import ni_measurementlink_service as nims +from ni_measurementlink_service.session_management import TypedSessionInformation service_directory = pathlib.Path(__file__).resolve().parent measurement_service = nims.MeasurementService( @@ -57,7 +58,7 @@ def measure( def _control_relays( - session_infos: Sequence[nims.session_management.TypedSessionInformation[niswitch.Session]], + session_infos: Sequence[TypedSessionInformation[niswitch.Session]], ) -> None: for session_info in session_infos: session_info.session.relay_control( From 0175e3f3f2a2174334a3017705b04a1ca0bbb68e Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Wed, 8 Nov 2023 12:44:56 +0530 Subject: [PATCH 17/19] tests: remove wait logic & standardize terminology --- .../nidigital_measurement/__init__.py | 26 ++++++++++--------- .../NISwitchMeasurement.serviceconfig | 4 +-- .../niswitch_measurement/__init__.py | 8 +++--- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/tests/utilities/nidigital_measurement/__init__.py b/tests/utilities/nidigital_measurement/__init__.py index 7d927a135..bd33797bf 100644 --- a/tests/utilities/nidigital_measurement/__init__.py +++ b/tests/utilities/nidigital_measurement/__init__.py @@ -1,11 +1,12 @@ """NI-Digital MeasurementLink test service.""" import pathlib +from itertools import groupby from typing import Iterable, Sequence, Tuple, Union import nidigital import ni_measurementlink_service as nims -from ni_measurementlink_service.session_management import TypedSessionInformation +from ni_measurementlink_service.session_management import TypedConnection, TypedSessionInformation service_directory = pathlib.Path(__file__).resolve().parent measurement_service = nims.MeasurementService( @@ -37,22 +38,23 @@ def measure( with measurement_service.context.reserve_sessions(pin_names) as reservation: with reservation.initialize_nidigital_sessions() as session_infos: connections = reservation.get_nidigital_connections(pin_names) - assert all([session_info is not None for session_info in session_infos]) + assert all([session_info.session is not None for session_info in session_infos]) passing_sites, failing_sites = _burst_spi_pattern(session_infos) - site0_connections, site1_connections = [], [] - # Assumption: All pins connected to site0 belongs to one session and - # all the pins connected to site1 belongs to the other session. - for connection in connections: - if connection.channel_name.startswith("site0"): - site0_connections.append(connection.channel_name) - elif connection.channel_name.startswith("site1"): - site1_connections.append(connection.channel_name) + def key_func(connection: TypedConnection[nidigital.Session]): + return connection.session_info.session_name + + connections_by_session = [ + list(g) for _, g in groupby(sorted(connections, key=key_func), key=key_func) + ] return ( [session_info.session_name for session_info in session_infos], [session_info.resource_name for session_info in session_infos], [session_info.channel_list for session_info in session_infos], - [", ".join(site0_connections), ", ".join(site1_connections)], + [ + ", ".join(conn.channel_name for conn in conns) + for conns in connections_by_session + ], list(passing_sites), list(failing_sites), ) @@ -60,7 +62,7 @@ def measure( with measurement_service.context.reserve_session(pin_names) as reservation: with reservation.initialize_nidigital_session() as session_info: connection = reservation.get_nidigital_connection(list(pin_names)[0]) - assert session_info is not None + assert session_info.session is not None passing_sites, failing_sites = _burst_spi_pattern([session_info]) return ( diff --git a/tests/utilities/niswitch_measurement/NISwitchMeasurement.serviceconfig b/tests/utilities/niswitch_measurement/NISwitchMeasurement.serviceconfig index 91193c821..3e3cd5afd 100644 --- a/tests/utilities/niswitch_measurement/NISwitchMeasurement.serviceconfig +++ b/tests/utilities/niswitch_measurement/NISwitchMeasurement.serviceconfig @@ -1,7 +1,7 @@ { "services": [ { - "displayName": "NI-Switch Measurement (Py)", + "displayName": "NI-SWITCH Measurement (Py)", "serviceClass": "ni.tests.NISwitchMeasurement_Python", "descriptionUrl": "", "providedInterfaces": [ @@ -10,7 +10,7 @@ ], "path": "start.bat", "annotations": { - "ni/service.description": "NI-Switch MeasurementLink test service.", + "ni/service.description": "NI-SWITCH MeasurementLink test service.", "ni/service.collection": "NI.Tests", "ni/service.tags": [] } diff --git a/tests/utilities/niswitch_measurement/__init__.py b/tests/utilities/niswitch_measurement/__init__.py index 593eb4fb0..f31e8d68e 100644 --- a/tests/utilities/niswitch_measurement/__init__.py +++ b/tests/utilities/niswitch_measurement/__init__.py @@ -1,4 +1,4 @@ -"""NI-Switch MeasurementLink test service.""" +"""NI-SWITCH MeasurementLink test service.""" import pathlib from typing import Iterable, Sequence, Tuple @@ -28,12 +28,12 @@ def measure( relay_names: Iterable[str], multi_session: bool, ) -> Tuple[Iterable[str], Iterable[str], Iterable[str], Iterable[str]]: - """NI-Switch MeasurementLink test service.""" + """NI-SWITCH MeasurementLink test service.""" if multi_session: with measurement_service.context.reserve_sessions(relay_names) as reservation: with reservation.initialize_niswitch_sessions() as session_infos: connections = reservation.get_niswitch_connections(relay_names) - assert all([session_info is not None for session_info in session_infos]) + assert all([session_info.session is not None for session_info in session_infos]) _control_relays(session_infos) return ( @@ -46,7 +46,7 @@ def measure( with measurement_service.context.reserve_session(relay_names) as reservation: with reservation.initialize_niswitch_session() as session_info: connection = reservation.get_niswitch_connection(list(relay_names)[0]) - assert session_info is not None + assert session_info.session is not None _control_relays([session_info]) return ( From 9a065aca96cb7d818e309b9b0b61d7aaf5c2d815 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Wed, 8 Nov 2023 12:48:41 +0530 Subject: [PATCH 18/19] fix: lint errors --- tests/utilities/nidigital_measurement/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/utilities/nidigital_measurement/__init__.py b/tests/utilities/nidigital_measurement/__init__.py index bd33797bf..bcb536319 100644 --- a/tests/utilities/nidigital_measurement/__init__.py +++ b/tests/utilities/nidigital_measurement/__init__.py @@ -40,6 +40,7 @@ def measure( connections = reservation.get_nidigital_connections(pin_names) assert all([session_info.session is not None for session_info in session_infos]) passing_sites, failing_sites = _burst_spi_pattern(session_infos) + def key_func(connection: TypedConnection[nidigital.Session]): return connection.session_info.session_name From 2e5b9b7f393fc98c661d1d4f1c8c7281d412d192 Mon Sep 17 00:00:00 2001 From: Jayaseelan James Date: Wed, 8 Nov 2023 15:06:03 +0530 Subject: [PATCH 19/19] fix: more inner method to private method due to mypy error --- tests/utilities/nidigital_measurement/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/utilities/nidigital_measurement/__init__.py b/tests/utilities/nidigital_measurement/__init__.py index bcb536319..0cb3c709b 100644 --- a/tests/utilities/nidigital_measurement/__init__.py +++ b/tests/utilities/nidigital_measurement/__init__.py @@ -40,12 +40,8 @@ def measure( connections = reservation.get_nidigital_connections(pin_names) assert all([session_info.session is not None for session_info in session_infos]) passing_sites, failing_sites = _burst_spi_pattern(session_infos) - - def key_func(connection: TypedConnection[nidigital.Session]): - return connection.session_info.session_name - connections_by_session = [ - list(g) for _, g in groupby(sorted(connections, key=key_func), key=key_func) + list(g) for _, g in groupby(sorted(connections, key=_key_func), key=_key_func) ] return ( @@ -134,3 +130,7 @@ def _resolve_relative_path( return file_path else: return (directory_path / file_path).resolve() + + +def _key_func(conn: TypedConnection[nidigital.Session]) -> str: + return conn.session_info.session_name