Add regression tests for the Medium findings resolved in this series: - AbCipDataType_maps_large_integer_types (theory: LInt→Int64, ULInt→UInt64, UDInt→UInt32) and Read_UDInt_tag_returns_uint_value_not_negative_wrapped_int cover Driver.AbCip-004. - Structure_parent_tag_is_not_readable_after_member_fan_out, InitializeAsync_throws_on_duplicate_tag_name, and InitializeAsync_throws_when_member_name_collides_with_independent_tag cover Driver.AbCip-005. - Read_failure_evicts_runtime_so_next_read_creates_fresh_handle covers Driver.AbCip-010. AbCipDriverTests.AbCipDataType_maps_atomics_to_driver_types extended with LInt/ULInt/UDInt assertions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
120 lines
4.4 KiB
C#
120 lines
4.4 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.AbCip;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.AbCip.PlcFamilies;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests;
|
|
|
|
[Trait("Category", "Unit")]
|
|
public sealed class AbCipDriverTests
|
|
{
|
|
[Fact]
|
|
public void DriverType_is_AbCip()
|
|
{
|
|
var drv = new AbCipDriver(new AbCipDriverOptions(), "drv-1");
|
|
drv.DriverType.ShouldBe("AbCip");
|
|
drv.DriverInstanceId.ShouldBe("drv-1");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_with_empty_devices_succeeds_and_marks_healthy()
|
|
{
|
|
var drv = new AbCipDriver(new AbCipDriverOptions(), "drv-1");
|
|
await drv.InitializeAsync("{}", CancellationToken.None);
|
|
drv.GetHealth().State.ShouldBe(DriverState.Healthy);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_registers_each_device_with_its_family_profile()
|
|
{
|
|
var drv = new AbCipDriver(new AbCipDriverOptions
|
|
{
|
|
Devices =
|
|
[
|
|
new AbCipDeviceOptions("ab://10.0.0.5/1,0", AbCipPlcFamily.ControlLogix),
|
|
new AbCipDeviceOptions("ab://10.0.0.6/", AbCipPlcFamily.Micro800),
|
|
],
|
|
}, "drv-1");
|
|
|
|
await drv.InitializeAsync("{}", CancellationToken.None);
|
|
|
|
drv.DeviceCount.ShouldBe(2);
|
|
drv.GetDeviceState("ab://10.0.0.5/1,0")!.Profile.ShouldBe(AbCipPlcFamilyProfile.ControlLogix);
|
|
drv.GetDeviceState("ab://10.0.0.6/")!.Profile.ShouldBe(AbCipPlcFamilyProfile.Micro800);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_with_malformed_host_address_faults()
|
|
{
|
|
var drv = new AbCipDriver(new AbCipDriverOptions
|
|
{
|
|
Devices = [new AbCipDeviceOptions("not-a-valid-address")],
|
|
}, "drv-1");
|
|
|
|
await Should.ThrowAsync<InvalidOperationException>(
|
|
() => drv.InitializeAsync("{}", CancellationToken.None));
|
|
drv.GetHealth().State.ShouldBe(DriverState.Faulted);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ShutdownAsync_clears_devices_and_marks_unknown()
|
|
{
|
|
var drv = new AbCipDriver(new AbCipDriverOptions
|
|
{
|
|
Devices = [new AbCipDeviceOptions("ab://10.0.0.5/1,0")],
|
|
}, "drv-1");
|
|
|
|
await drv.InitializeAsync("{}", CancellationToken.None);
|
|
drv.DeviceCount.ShouldBe(1);
|
|
|
|
await drv.ShutdownAsync(CancellationToken.None);
|
|
drv.DeviceCount.ShouldBe(0);
|
|
drv.GetHealth().State.ShouldBe(DriverState.Unknown);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReinitializeAsync_cycles_devices()
|
|
{
|
|
var drv = new AbCipDriver(new AbCipDriverOptions
|
|
{
|
|
Devices = [new AbCipDeviceOptions("ab://10.0.0.5/1,0")],
|
|
}, "drv-1");
|
|
|
|
await drv.InitializeAsync("{}", CancellationToken.None);
|
|
await drv.ReinitializeAsync("{}", CancellationToken.None);
|
|
|
|
drv.DeviceCount.ShouldBe(1);
|
|
drv.GetHealth().State.ShouldBe(DriverState.Healthy);
|
|
}
|
|
|
|
[Fact]
|
|
public void Family_profiles_expose_expected_defaults()
|
|
{
|
|
AbCipPlcFamilyProfile.ControlLogix.LibplctagPlcAttribute.ShouldBe("controllogix");
|
|
AbCipPlcFamilyProfile.ControlLogix.DefaultConnectionSize.ShouldBe(4002);
|
|
AbCipPlcFamilyProfile.ControlLogix.DefaultCipPath.ShouldBe("1,0");
|
|
|
|
AbCipPlcFamilyProfile.Micro800.DefaultCipPath.ShouldBe(""); // no backplane routing
|
|
AbCipPlcFamilyProfile.Micro800.SupportsRequestPacking.ShouldBeFalse();
|
|
AbCipPlcFamilyProfile.Micro800.SupportsConnectedMessaging.ShouldBeFalse();
|
|
|
|
AbCipPlcFamilyProfile.CompactLogix.DefaultConnectionSize.ShouldBe(504);
|
|
AbCipPlcFamilyProfile.GuardLogix.LibplctagPlcAttribute.ShouldBe("controllogix");
|
|
}
|
|
|
|
[Fact]
|
|
public void AbCipDataType_maps_atomics_to_driver_types()
|
|
{
|
|
AbCipDataType.Bool.ToDriverDataType().ShouldBe(DriverDataType.Boolean);
|
|
AbCipDataType.DInt.ToDriverDataType().ShouldBe(DriverDataType.Int32);
|
|
AbCipDataType.Real.ToDriverDataType().ShouldBe(DriverDataType.Float32);
|
|
AbCipDataType.LReal.ToDriverDataType().ShouldBe(DriverDataType.Float64);
|
|
AbCipDataType.String.ToDriverDataType().ShouldBe(DriverDataType.String);
|
|
// Driver.AbCip-004: 64-bit and unsigned-32-bit types must map to their correct equivalents.
|
|
AbCipDataType.LInt.ToDriverDataType().ShouldBe(DriverDataType.Int64);
|
|
AbCipDataType.ULInt.ToDriverDataType().ShouldBe(DriverDataType.UInt64);
|
|
AbCipDataType.UDInt.ToDriverDataType().ShouldBe(DriverDataType.UInt32);
|
|
}
|
|
}
|