Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
568fce9
Add new TLS system tests
ryanwixon-emerson Sep 9, 2026
e1f346b
Add sad path tests
ryanwixon-emerson Sep 10, 2026
40140b9
Resolve flake errors
ryanwixon-emerson Sep 10, 2026
e771017
Tests are passing locally
ryanwixon-emerson Sep 11, 2026
09768c6
Rerun system tests
ryanwixon-emerson Sep 14, 2026
62e5ef1
Path client config, slight test tweaks
ryanwixon-emerson Sep 14, 2026
d35c662
Remove unusued import
ryanwixon-emerson Sep 14, 2026
8e7ba3b
[TEMP] Revert exchange_certs patch
ryanwixon-emerson Sep 14, 2026
f74b0cd
Change exchange_certificates invocation
ryanwixon-emerson Sep 14, 2026
30aa42b
Try to set user environemnt var manually
ryanwixon-emerson Sep 14, 2026
deee6d7
Fix Assertion problem and no-op nitlsconfigtest stuff on Linux
ryanwixon-emerson Sep 14, 2026
d05a0d3
Formatter fix
ryanwixon-emerson Sep 14, 2026
09233f9
Use sysnative so 32 bit tests can see nitlsconfig
ryanwixon-emerson Sep 14, 2026
6a832f8
Fix warnings
ryanwixon-emerson Sep 14, 2026
e4e608c
[Experimental] Try force disabling WOW64 redirection
ryanwixon-emerson Sep 14, 2026
b8806ec
[Experimental] Process wide redirection disabled
ryanwixon-emerson Sep 14, 2026
c85077d
[Experimental] Claude's "validated" fix?
ryanwixon-emerson Sep 14, 2026
fb07473
Clean up implementation (fully working?)
ryanwixon-emerson Sep 14, 2026
eff59b6
Rereun flakey test
ryanwixon-emerson Sep 15, 2026
d5d03d8
Run flakey test again
ryanwixon-emerson Sep 15, 2026
1ac846a
Run flakey test again
ryanwixon-emerson Sep 15, 2026
a538bf5
Implement nidcpower system tests
ryanwixon-emerson Sep 15, 2026
859f0d5
Implement nidigital and nifgen system tests
ryanwixon-emerson Sep 15, 2026
3e003c5
Add system tests for all remaining drivers
ryanwixon-emerson Sep 15, 2026
671cac0
Reorder tests
ryanwixon-emerson Sep 15, 2026
315c910
Refactor tests to address comments
ryanwixon-emerson Sep 16, 2026
b3710c7
Fix flake failure
ryanwixon-emerson Sep 16, 2026
8e2a08d
Flip the type of tests that run the full suite
ryanwixon-emerson Sep 16, 2026
984c5e8
Don't use nitlsconfig channel for NoTLS test
ryanwixon-emerson Sep 16, 2026
f46d93e
Now disable on 32-bit
ryanwixon-emerson Sep 16, 2026
00a3c96
Various improvements to address review comments
ryanwixon-emerson Sep 17, 2026
a87a556
Fix flake
ryanwixon-emerson Sep 17, 2026
6451175
Merge branch 'addTLS' into users/rwixon/addAllTLS
ryanwixon-emerson Sep 17, 2026
6b7dbca
Refactor to match the DMM changes
ryanwixon-emerson Sep 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/nidcpower/system_tests/grpc_server_config_tls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"address": "[::]",
"port": 31760,
"security": "ni-tls-config",
"feature_toggles": {
"ni-tls-config": true
}
}
145 changes: 97 additions & 48 deletions src/nidcpower/system_tests/test_system_nidcpower.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import grpc
import hightime
import nitlsconfig
import pytest

import nidcpower
Expand Down Expand Up @@ -37,7 +38,9 @@ def pytest_generate_tests(metafunc):
metafunc.parametrize('session', [True], indirect=True)


class SystemTests:
# Defines a subset of system tests to validate basic NI-DCPower functionality. This is run as a part of the full SystemTests class, and
# independently for test classes which do not require running the entire suite (TLS-enabled gRPC tests today).
class BasicValidationTests:
@pytest.fixture(scope='function')
def session(self, request, session_creation_kwargs):
"""Creates an NI-DCPower Session.
Expand Down Expand Up @@ -75,6 +78,50 @@ def session(self, request, session_creation_kwargs):
with nidcpower.Session(**init_args, **session_creation_kwargs) as simulated_session:
yield simulated_session

@pytest.mark.channels('0')
def test_measure(self, session):
session.source_mode = nidcpower.SourceMode.SINGLE_POINT
session.output_function = nidcpower.OutputFunction.DC_VOLTAGE
session.voltage_level_range = 6
session.voltage_level = 2
with session.initiate():
reading = session.measure(nidcpower.MeasurementTypes.VOLTAGE)
assert session.query_in_compliance() is False
assert reading == 2

@pytest.mark.channels('0')
def test_fetch_multiple(self, session):
session.source_mode = nidcpower.SourceMode.SINGLE_POINT
session.configure_aperture_time(0, nidcpower.ApertureTimeUnits.SECONDS)
session.voltage_level = 1
count = 10
session.measure_when = nidcpower.MeasureWhen.AUTOMATICALLY_AFTER_SOURCE_COMPLETE
with session.initiate():
measurements = session.fetch_multiple(count)
assert len(measurements) == count
assert isinstance(measurements[1].voltage, float)
assert isinstance(measurements[1].current, float)
assert measurements[1].in_compliance in [True, False]
assert measurements[1].voltage == 1.0
assert measurements[1].current == 0.00001

def test_measure_multiple(self, session):
with session.initiate():
# session is open to all 12 channels on the device
measurements = session.measure_multiple()
assert len(measurements) == 12
assert measurements[1].in_compliance is None
assert measurements[1].voltage == 0.0
assert measurements[1].current == 0.00001
# now a subset of the channels
measurements = session.channels[range(4)].measure_multiple()
assert len(measurements) == 4
assert measurements[1].in_compliance is None
assert measurements[1].voltage == 0.0
assert measurements[1].current == 0.00001


class SystemTests(BasicValidationTests):
def test_self_test(self, session):
session.self_test()

Expand Down Expand Up @@ -161,17 +208,6 @@ def test_disable(self, session):
session.disable()
assert channel.output_enabled is False

@pytest.mark.channels('0')
def test_measure(self, session):
session.source_mode = nidcpower.SourceMode.SINGLE_POINT
session.output_function = nidcpower.OutputFunction.DC_VOLTAGE
session.voltage_level_range = 6
session.voltage_level = 2
with session.initiate():
reading = session.measure(nidcpower.MeasurementTypes.VOLTAGE)
assert session.query_in_compliance() is False
assert reading == 2

@pytest.mark.channels('0')
def test_query_output_state(self, session):
with session.initiate():
Expand All @@ -192,37 +228,6 @@ def test_config_aperture_time(self, session):
aperture_time_in_range = abs(aperture_time - expected_aperture_time) <= max(1e-09 * max(abs(aperture_time), abs(expected_aperture_time)), 0.0) # https://stackoverflow.com/questions/5595425/what-is-the-best-way-to-compare-floats-for-almost-equality-in-python
assert aperture_time_in_range is True

@pytest.mark.channels('0')
def test_fetch_multiple(self, session):
session.source_mode = nidcpower.SourceMode.SINGLE_POINT
session.configure_aperture_time(0, nidcpower.ApertureTimeUnits.SECONDS)
session.voltage_level = 1
count = 10
session.measure_when = nidcpower.MeasureWhen.AUTOMATICALLY_AFTER_SOURCE_COMPLETE
with session.initiate():
measurements = session.fetch_multiple(count)
assert len(measurements) == count
assert isinstance(measurements[1].voltage, float)
assert isinstance(measurements[1].current, float)
assert measurements[1].in_compliance in [True, False]
assert measurements[1].voltage == 1.0
assert measurements[1].current == 0.00001

def test_measure_multiple(self, session):
with session.initiate():
# session is open to all 12 channels on the device
measurements = session.measure_multiple()
assert len(measurements) == 12
assert measurements[1].in_compliance is None
assert measurements[1].voltage == 0.0
assert measurements[1].current == 0.00001
# now a subset of the channels
measurements = session.channels[range(4)].measure_multiple()
assert len(measurements) == 4
assert measurements[1].in_compliance is None
assert measurements[1].voltage == 0.0
assert measurements[1].current == 0.00001

@pytest.mark.parametrize(
'resource_name,channels,independent_channels,measurement_channels,expected_measured_channel',
[
Expand Down Expand Up @@ -1074,7 +1079,8 @@ def test_multi_threading_ivi_synchronized_wrapper_releases_lock(self, session):

class TestLibrary(SystemTests):
@pytest.fixture(scope='class')
def session_creation_kwargs(self):
@classmethod
def session_creation_kwargs(cls):
return {}

@pytest.mark.resource_name("4190/0")
Expand All @@ -1096,17 +1102,19 @@ def test_lcr_compensation_data(self, session):
session.configure_lcr_compensation(compensation_data_bytes_from_file)


class TestGrpc(SystemTests):
class TestGrpcNoTLS(SystemTests):
@pytest.fixture(scope='class')
def grpc_channel(self):
@classmethod
def grpc_channel(cls):
current_directory = os.path.dirname(os.path.abspath(__file__))
config_file_path = os.path.join(current_directory, 'grpc_server_config.json')
config_file_path = os.path.join(current_directory, 'grpc_server_config_no_tls.json')
with system_test_utilities.GrpcServerProcess(config_file_path) as proc:
channel = grpc.insecure_channel(f"localhost:{proc.server_port}")
yield channel

@pytest.fixture(scope='class')
def session_creation_kwargs(self, grpc_channel):
@classmethod
def session_creation_kwargs(cls, grpc_channel):
grpc_options = nidcpower.GrpcSessionOptions(grpc_channel, "")
return {'grpc_options': grpc_options}

Expand All @@ -1121,3 +1129,44 @@ def test_configure_lcr_compensation(self, session):
session.configure_lcr_compensation([])
assert exc_info.value.args[0] == 'configure_lcr_compensation is not supported over gRPC'
assert str(exc_info.value) == 'configure_lcr_compensation is not supported over gRPC'


@pytest.mark.skipif(sys.maxsize < 2**32, reason="TLS configuration and certificate exchange scripts are not supported in 32-bit processes")
class TestGrpcSecuredTLS(BasicValidationTests):
@pytest.fixture(scope='class')
@classmethod
def grpc_channel(cls):
system_test_utilities.configure_tls_modes_secure(service="ni-grpc-device", server_host="localhost")
system_test_utilities.exchange_certificates("localhost")

current_directory = os.path.dirname(os.path.abspath(__file__))
config_file_path = os.path.join(current_directory, 'grpc_server_config_tls.json')
with system_test_utilities.GrpcServerProcess(config_file_path) as proc:
channel = nitlsconfig.create_grpc_device_channel('localhost', proc.server_port)
yield channel

@pytest.fixture(scope='class')
@classmethod
def session_creation_kwargs(cls, grpc_channel):
grpc_options = nidcpower.GrpcSessionOptions(grpc_channel, "")
return {'grpc_options': grpc_options}


@pytest.mark.skipif(sys.maxsize < 2**32, reason="TLS configuration and certificate exchange scripts are not supported in 32-bit processes")
class TestGrpcUnsecuredTLS(BasicValidationTests):
@pytest.fixture(scope='class')
@classmethod
def grpc_channel(cls):
system_test_utilities.configure_tls_modes_insecure(service="ni-grpc-device", server_host="localhost")

current_directory = os.path.dirname(os.path.abspath(__file__))
config_file_path = os.path.join(current_directory, 'grpc_server_config_tls.json')
with system_test_utilities.GrpcServerProcess(config_file_path) as proc:
channel = nitlsconfig.create_grpc_device_channel('localhost', proc.server_port)
yield channel

@pytest.fixture(scope='class')
@classmethod
def session_creation_kwargs(cls, grpc_channel):
grpc_options = nidcpower.GrpcSessionOptions(grpc_channel, "")
return {'grpc_options': grpc_options}
8 changes: 8 additions & 0 deletions src/nidigital/system_tests/grpc_server_config_tls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"address": "[::]",
"port": 31761,
"security": "ni-tls-config",
"feature_toggles": {
"ni-tls-config": true
}
}
Loading
Loading