diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyArrayWriteSupportParityTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyArrayWriteSupportParityTests.cs new file mode 100644 index 00000000..acae0cdd --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyArrayWriteSupportParityTests.cs @@ -0,0 +1,58 @@ +using Shouldly; +using Xunit; +using ZB.MOM.WW.OtOpcUa.Core.Abstractions; + +namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests; + +/// +/// Pins 's AbLegacy row against the REAL array encoder +/// (), the same way the Modbus and S7 parity tests do: for every element +/// type the server must advertise an array node writable exactly when the driver encodes it. +/// +/// +/// ⚠️ SKIPPED until follow-up F1 lands: the driver gained an array encoder in this lane (review 07 U10), but +/// (Core.Abstractions, not owned by the AbLegacy lane) still lists AbLegacy as +/// scalar-only, so every AbLegacy array node stays read-only and the encoder is unreachable from OPC UA. When F1 +/// adds the row, delete the Skip; this test then holds the table and the encoder together. +/// +[Trait("Category", "Unit")] +public sealed class AbLegacyArrayWriteSupportParityTests +{ + public static TheoryData AllTypes() => new(Enum.GetValues()); + + private static string AddressFor(AbLegacyDataType t) => t switch + { + AbLegacyDataType.Float => "F8:0", + AbLegacyDataType.Long => "L9:0", + AbLegacyDataType.Bit => "B3:0", + AbLegacyDataType.String => "ST9:0", + AbLegacyDataType.TimerElement => "T4:0", + AbLegacyDataType.CounterElement => "C5:0", + AbLegacyDataType.ControlElement => "R6:0", + _ => "N7:0", + }; + + [Theory(Skip = "F1: DriverArrayWriteSupport has no AbLegacy row yet (Core.Abstractions is outside this lane)")] + [MemberData(nameof(AllTypes))] + public void Table_agrees_with_the_array_encoder(AbLegacyDataType dataType) + { + var json = $$"""{"address":"{{AddressFor(dataType)}}","dataType":"{{dataType}}","isArray":true,"arrayLength":2}"""; + var encoderAccepts = AbLegacyTagDefinitionFactory.FromTagConfig(json, "p", out _) + && AbLegacyArrayWriteEncoder.TryConvert(dataType, new object[] { 1, 1 }, 2, out _) == AbLegacyStatusMapper.Good; + + DriverArrayWriteSupport.Supports(DriverTypeNames.AbLegacy, "Int16", json).ShouldBe(encoderAccepts, dataType.ToString()); + } + + /// What the encoder accepts today — the row F1 must add (Int, AnalogInt, Long, Float). + [Theory] + [InlineData(AbLegacyDataType.Int, true)] + [InlineData(AbLegacyDataType.AnalogInt, true)] + [InlineData(AbLegacyDataType.Long, true)] + [InlineData(AbLegacyDataType.Float, true)] + [InlineData(AbLegacyDataType.Bit, false)] + [InlineData(AbLegacyDataType.String, false)] + [InlineData(AbLegacyDataType.TimerElement, false)] + public void Encoder_accepts_exactly_the_flat_numeric_element_types(AbLegacyDataType dataType, bool accepts) => + (AbLegacyArrayWriteEncoder.TryConvert(dataType, new object[] { 1, 1 }, 2, out _) == AbLegacyStatusMapper.Good) + .ShouldBe(accepts); +}