using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime; namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests.Runtime; /// /// Gitea #610 — pins 's Galaxy row against the REAL write encoder /// (). The driver receives the CLR array type of the node's DataType and no /// other: NOT because the SDK checks it (it skips its own type check on a node with an OnWriteValue /// handler, which every writable equipment node has) but because OtOpcUaNodeManager.EvaluateEquipmentWriteStructure /// refuses an array whose element type does not match the node's DataType exactly (#610 review MAJOR-1). So /// for every Tag.DataType the server can materialise, the node is advertised writable exactly when the /// encoder accepts that array type. /// [Trait("Category", "Unit")] public sealed class GalaxyArrayWriteSupportParityTests { // Tag.DataType name → the CLR array the OPC UA stack delivers for a 1-D write of that type. private static readonly Dictionary ClrArrays = new() { ["Boolean"] = new[] { true, false }, ["SByte"] = new sbyte[] { 1, 2 }, ["Byte"] = new byte[] { 1, 2 }, ["Int16"] = new short[] { 1, 2 }, ["UInt16"] = new ushort[] { 1, 2 }, ["Int32"] = new[] { 1, 2 }, ["UInt32"] = new uint[] { 1, 2 }, ["Int64"] = new long[] { 1, 2 }, ["UInt64"] = new ulong[] { 1, 2 }, ["Float"] = new[] { 1f, 2f }, ["Double"] = new[] { 1d, 2d }, ["String"] = new[] { "a", "b" }, ["DateTime"] = new[] { DateTime.UtcNow, DateTime.UtcNow }, }; public static TheoryData DataTypes() => new(ClrArrays.Keys); [Theory] [MemberData(nameof(DataTypes))] public void Table_agrees_with_MxValueEncoder(string dataType) { bool encoderAccepts; try { MxValueEncoder.Encode(ClrArrays[dataType]); encoderAccepts = true; } catch (ArgumentException) { encoderAccepts = false; } // UInt32 / UInt64 used to be a hand-written exclusion here, because CLR array covariance let uint[] / // ulong[] match the encoder's int[] / long[] arms and be encoded by reinterpreting their bits. The encoder // now refuses them itself, so the table and the encoder agree with no special case. DriverArrayWriteSupport.Supports(DriverTypeNames.Galaxy, dataType, "{\"FullName\":\"Obj.Attr\",\"isArray\":true,\"arrayLength\":2}") .ShouldBe(encoderAccepts, dataType); } /// Positive control: the theory above sees both answers, so it is not vacuously all-refused. [Fact] public void The_matrix_contains_both_answers() => ClrArrays.Keys .Select(dt => DriverArrayWriteSupport.Supports(DriverTypeNames.Galaxy, dt, "{\"isArray\":true,\"arrayLength\":2}")) .ToHashSet() .ShouldBe(new[] { true, false }, ignoreOrder: true); }