docs: backfill XML documentation across 756 files
v2-ci / build (push) Failing after 1m43s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
v2-ci / build (push) Failing after 1m43s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Adds <summary>, <param>, <typeparam>, and <inheritdoc/> tags to public members surfaced by commentchecker — resolves 5,847 of 5,869 issues (99.6%) across three /fixdocs passes.
This commit is contained in:
+20
@@ -16,6 +16,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
{
|
||||
// ── Parser: empty trailing-field rejection (Driver.Modbus.Addressing-002) ──────────────
|
||||
|
||||
/// <summary>Verifies that a 3-field address with an empty third field is rejected.</summary>
|
||||
[Fact]
|
||||
public void Parser_3field_empty_third_field_rejected()
|
||||
{
|
||||
@@ -27,6 +28,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
error!.ShouldContain("empty");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a 4-field address with an empty third field (byte order) accepts the default.</summary>
|
||||
[Fact]
|
||||
public void Parser_4field_empty_third_field_accepted_as_default_order()
|
||||
{
|
||||
@@ -40,6 +42,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
|
||||
// ── Parser: misplaced type code gives better diagnostic (Driver.Modbus.Addressing-003) ─
|
||||
|
||||
/// <summary>Verifies that a misplaced type code in the third field produces a helpful error.</summary>
|
||||
[Fact]
|
||||
public void Parser_3field_misplaced_type_in_third_field_gives_helpful_error()
|
||||
{
|
||||
@@ -54,6 +57,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
|
||||
// ── Parser: multi-dot input (Driver.Modbus.Addressing-004) ──────────────────────────────
|
||||
|
||||
/// <summary>Verifies that addresses with multiple dots are rejected with a clear error.</summary>
|
||||
[Fact]
|
||||
public void Parser_multi_dot_input_rejected_with_clear_error()
|
||||
{
|
||||
@@ -63,6 +67,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
error.ShouldNotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a decimal point typo produces a precise error.</summary>
|
||||
[Fact]
|
||||
public void Parser_decimal_point_typo_400_01_gives_precise_error()
|
||||
{
|
||||
@@ -76,6 +81,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
|
||||
// ── DirectLogicAddress overflow and boundary (Driver.Modbus.Addressing-008) ────────────
|
||||
|
||||
/// <summary>Verifies that user V-memory overflow throws an OverflowException.</summary>
|
||||
[Fact]
|
||||
public void UserVMemoryToPdu_overflow_throws_OverflowException()
|
||||
{
|
||||
@@ -83,6 +89,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
Should.Throw<OverflowException>(() => DirectLogicAddress.UserVMemoryToPdu("V200000"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that octal offset overflow via Y throws an OverflowException.</summary>
|
||||
[Fact]
|
||||
public void AddOctalOffset_overflow_via_Y_throws_OverflowException()
|
||||
{
|
||||
@@ -92,6 +99,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
Should.Throw<OverflowException>(() => DirectLogicAddress.YOutputToCoil("Y174000"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that octal offset overflow via C throws an OverflowException.</summary>
|
||||
[Fact]
|
||||
public void AddOctalOffset_overflow_via_C_throws_OverflowException()
|
||||
{
|
||||
@@ -100,6 +108,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
Should.Throw<OverflowException>(() => DirectLogicAddress.CRelayToCoil("C172000"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that SystemVMemoryToPdu is exercised and returns the correct base.</summary>
|
||||
[Fact]
|
||||
public void SystemVMemoryToPdu_is_exercised_and_returns_correct_base()
|
||||
{
|
||||
@@ -109,6 +118,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
DirectLogicAddress.SystemVMemoryToPdu(1).ShouldBe((ushort)(DirectLogicAddress.SystemVMemoryBasePdu + 1));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that system V-memory overflow throws an OverflowException.</summary>
|
||||
[Fact]
|
||||
public void SystemVMemoryToPdu_overflow_throws_OverflowException()
|
||||
{
|
||||
@@ -119,6 +129,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
|
||||
// ── MelsecAddress overflow / boundary (Driver.Modbus.Addressing-008) ─────────────────
|
||||
|
||||
/// <summary>Verifies that MELSEC hex address parsing overflow throws an OverflowException.</summary>
|
||||
[Fact]
|
||||
public void MelsecAddress_ParseHex_overflow_throws_OverflowException()
|
||||
{
|
||||
@@ -126,6 +137,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
Should.Throw<OverflowException>(() => MelsecAddress.XInputToDiscrete("X10000", MelsecFamily.Q_L_iQR));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that MELSEC D-register to holding overflow throws an exception.</summary>
|
||||
[Fact]
|
||||
public void MelsecAddress_DRegisterToHolding_overflow_throws_OverflowException()
|
||||
{
|
||||
@@ -133,6 +145,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
Should.Throw<ArgumentException>(() => MelsecAddress.DRegisterToHolding("D65536"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that MELSEC M-relay to coil overflow throws an OverflowException.</summary>
|
||||
[Fact]
|
||||
public void MelsecAddress_MRelayToCoil_overflow_throws_OverflowException()
|
||||
{
|
||||
@@ -140,6 +153,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
Should.Throw<OverflowException>(() => MelsecAddress.MRelayToCoil("M65535", mBankBase: 1));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that MELSEC D-register bank base overflow throws an OverflowException.</summary>
|
||||
[Fact]
|
||||
public void MelsecAddress_DRegisterToHolding_bank_base_overflow_throws_OverflowException()
|
||||
{
|
||||
@@ -156,6 +170,8 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
// unhandled exception out of a TryParse method. These tests assert the defensive contract
|
||||
// across a broad set of pathological inputs.
|
||||
|
||||
/// <summary>Verifies that DL205 address parsing never throws and returns a structured error.</summary>
|
||||
/// <param name="addr">The address string to parse.</param>
|
||||
[Theory]
|
||||
[InlineData("V")] // V prefix with no digits
|
||||
[InlineData("V99999999999999")] // overflow in user V-memory octal decode
|
||||
@@ -181,6 +197,8 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
error.ShouldNotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that MELSEC address parsing never throws and returns a structured error.</summary>
|
||||
/// <param name="addr">The address string to parse.</param>
|
||||
[Theory]
|
||||
[InlineData("D")] // D prefix alone — no digits
|
||||
[InlineData("D-1")] // negative — would fail ushort.TryParse, must not throw
|
||||
@@ -207,6 +225,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
// is the canonical config path. These tests pin that contract so a future grammar change
|
||||
// can't quietly add a token that conflicts with the array-count slot.
|
||||
|
||||
/// <summary>Verifies that the parser's STR grammar does not carry StringByteOrder.</summary>
|
||||
[Fact]
|
||||
public void Parser_STR_grammar_does_not_carry_StringByteOrder()
|
||||
{
|
||||
@@ -224,6 +243,7 @@ public sealed class ModbusAddressEdgeCaseTests
|
||||
.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the parser rejects unknown string byte order tokens in the grammar.</summary>
|
||||
[Fact]
|
||||
public void Parser_rejects_unknown_string_byte_order_token_in_grammar()
|
||||
{
|
||||
|
||||
+46
@@ -9,6 +9,11 @@ public sealed class ModbusAddressParserTests
|
||||
{
|
||||
// ----- Bare Modicon-only forms inherit #136 behaviour; one sanity row per region. -----
|
||||
|
||||
/// <summary>Verifies that bare Modicon address forms inherit their data type from region defaults.</summary>
|
||||
/// <param name="addr">The Modicon address string to parse.</param>
|
||||
/// <param name="region">The expected Modbus region.</param>
|
||||
/// <param name="offset">The expected zero-based register offset.</param>
|
||||
/// <param name="type">The expected data type inferred from the region.</param>
|
||||
[Theory]
|
||||
[InlineData("40001", ModbusRegion.HoldingRegisters, 0, ModbusDataType.Int16)]
|
||||
[InlineData("400001", ModbusRegion.HoldingRegisters, 0, ModbusDataType.Int16)]
|
||||
@@ -29,6 +34,10 @@ public sealed class ModbusAddressParserTests
|
||||
|
||||
// ----- Mnemonic forms — HR / IR / C / DI -----
|
||||
|
||||
/// <summary>Verifies that mnemonic region forms (HR, IR, C, DI) parse correctly.</summary>
|
||||
/// <param name="addr">The mnemonic address string to parse.</param>
|
||||
/// <param name="region">The expected Modbus region.</param>
|
||||
/// <param name="offset">The expected zero-based register offset.</param>
|
||||
[Theory]
|
||||
[InlineData("HR1", ModbusRegion.HoldingRegisters, 0)]
|
||||
[InlineData("HR65536", ModbusRegion.HoldingRegisters, 65535)]
|
||||
@@ -46,6 +55,9 @@ public sealed class ModbusAddressParserTests
|
||||
|
||||
// ----- Bit suffix .N -----
|
||||
|
||||
/// <summary>Verifies that bit suffix notation sets the expected bit position and data type.</summary>
|
||||
/// <param name="addr">The address string with bit suffix to parse.</param>
|
||||
/// <param name="expectedBit">The expected bit index within the register.</param>
|
||||
[Theory]
|
||||
[InlineData("40001.0", 0)]
|
||||
[InlineData("40001.5", 5)]
|
||||
@@ -58,6 +70,7 @@ public sealed class ModbusAddressParserTests
|
||||
p.DataType.ShouldBe(ModbusDataType.BitInRegister);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that bit suffix notation combined with explicit type specification is rejected.</summary>
|
||||
[Fact]
|
||||
public void Bit_Plus_Explicit_Type_Rejected()
|
||||
{
|
||||
@@ -65,6 +78,7 @@ public sealed class ModbusAddressParserTests
|
||||
.Message.ShouldContain("Bit suffix");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that bit positions outside the valid 0-15 range are rejected.</summary>
|
||||
[Fact]
|
||||
public void Bit_Above_15_Rejected()
|
||||
{
|
||||
@@ -80,6 +94,9 @@ public sealed class ModbusAddressParserTests
|
||||
// `:BCD_32` for 32-bit BCD (Ignition HRBCD_32). The pre-#146 `:DI`/`:L`/`:UDI`/`:UL`/
|
||||
// `:LI`/`:ULI`/`:LBCD` aliases are removed — they conflict with the Wonderware mapping
|
||||
// and have no clear vendor precedent.
|
||||
/// <summary>Verifies that type code suffixes parse to the expected data type.</summary>
|
||||
/// <param name="addr">The address string with type code suffix to parse.</param>
|
||||
/// <param name="expected">The expected data type.</param>
|
||||
[Theory]
|
||||
[InlineData("40001:BOOL", ModbusDataType.Bool)]
|
||||
[InlineData("40001:S", ModbusDataType.Int16)]
|
||||
@@ -99,6 +116,8 @@ public sealed class ModbusAddressParserTests
|
||||
ModbusAddressParser.Parse(addr).DataType.ShouldBe(expected);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that pre-#146 type code aliases are rejected with a clear diagnostic.</summary>
|
||||
/// <param name="addr">The address string containing a removed alias type code.</param>
|
||||
[Theory]
|
||||
[InlineData("40001:DI")] // pre-#146 alias removed
|
||||
[InlineData("40001:L")]
|
||||
@@ -116,6 +135,9 @@ public sealed class ModbusAddressParserTests
|
||||
.Message.ShouldContain("Unknown type code");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that string type notation carries the expected string length.</summary>
|
||||
/// <param name="addr">The address string with STR type and length to parse.</param>
|
||||
/// <param name="expectedLen">The expected string length.</param>
|
||||
[Theory]
|
||||
[InlineData("40001:STR1", 1)]
|
||||
[InlineData("40001:STR20", 20)]
|
||||
@@ -127,6 +149,7 @@ public sealed class ModbusAddressParserTests
|
||||
p.StringLength.ShouldBe((ushort)expectedLen);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that string type notation without length is rejected.</summary>
|
||||
[Fact]
|
||||
public void STR_Without_Length_Rejected()
|
||||
{
|
||||
@@ -134,6 +157,7 @@ public sealed class ModbusAddressParserTests
|
||||
.Message.ShouldContain("STR");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that string length of zero is rejected.</summary>
|
||||
[Fact]
|
||||
public void STR_Length_Zero_Rejected()
|
||||
{
|
||||
@@ -141,6 +165,7 @@ public sealed class ModbusAddressParserTests
|
||||
.Message.ShouldContain("positive");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unknown type codes are rejected with a catalog of valid codes.</summary>
|
||||
[Fact]
|
||||
public void Unknown_Type_Code_Rejected_With_Catalog()
|
||||
{
|
||||
@@ -150,6 +175,7 @@ public sealed class ModbusAddressParserTests
|
||||
|
||||
// ----- Region-type compatibility -----
|
||||
|
||||
/// <summary>Verifies that float types are rejected for coil region.</summary>
|
||||
[Fact]
|
||||
public void Coils_With_Float_Type_Rejected()
|
||||
{
|
||||
@@ -157,6 +183,7 @@ public sealed class ModbusAddressParserTests
|
||||
.Message.ShouldContain("only supports Bool");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that integer types are rejected for discrete input region.</summary>
|
||||
[Fact]
|
||||
public void DiscreteInputs_With_Int_Type_Rejected()
|
||||
{
|
||||
@@ -166,6 +193,9 @@ public sealed class ModbusAddressParserTests
|
||||
|
||||
// ----- Byte order modifiers — all four -----
|
||||
|
||||
/// <summary>Verifies that byte order modifiers parse to the expected ordering.</summary>
|
||||
/// <param name="addr">The address string with byte order modifier to parse.</param>
|
||||
/// <param name="expected">The expected byte order.</param>
|
||||
[Theory]
|
||||
[InlineData("40001:F:ABCD", ModbusByteOrder.BigEndian)]
|
||||
[InlineData("40001:F:CDAB", ModbusByteOrder.WordSwap)]
|
||||
@@ -177,6 +207,7 @@ public sealed class ModbusAddressParserTests
|
||||
ModbusAddressParser.Parse(addr).ByteOrder.ShouldBe(expected);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unknown byte order codes are rejected with a catalog of valid codes.</summary>
|
||||
[Fact]
|
||||
public void Unknown_Byte_Order_Rejected_With_Catalog()
|
||||
{
|
||||
@@ -184,6 +215,7 @@ public sealed class ModbusAddressParserTests
|
||||
.Message.ShouldContain("Valid: ABCD, CDAB, BADC, DCBA");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that empty byte order field defaults to big-endian.</summary>
|
||||
[Fact]
|
||||
public void Empty_Order_Field_Means_Default()
|
||||
{
|
||||
@@ -198,6 +230,9 @@ public sealed class ModbusAddressParserTests
|
||||
|
||||
// ----- Array count -----
|
||||
|
||||
/// <summary>Verifies that array count notation parses to the expected count.</summary>
|
||||
/// <param name="addr">The address string with array count to parse.</param>
|
||||
/// <param name="expectedCount">The expected array element count.</param>
|
||||
[Theory]
|
||||
[InlineData("40001:I:ABCD:1", 1)]
|
||||
[InlineData("40001:F:5", 5)]
|
||||
@@ -208,6 +243,7 @@ public sealed class ModbusAddressParserTests
|
||||
ModbusAddressParser.Parse(addr).ArrayCount.ShouldBe(expectedCount);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that array count of zero is rejected.</summary>
|
||||
[Fact]
|
||||
public void Array_Count_Zero_Rejected()
|
||||
{
|
||||
@@ -215,6 +251,7 @@ public sealed class ModbusAddressParserTests
|
||||
.Message.ShouldContain("positive");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that non-numeric array count is rejected.</summary>
|
||||
[Fact]
|
||||
public void Array_Count_NonNumeric_Rejected()
|
||||
{
|
||||
@@ -222,6 +259,7 @@ public sealed class ModbusAddressParserTests
|
||||
.Message.ShouldContain("positive");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that bit suffix combined with array count is rejected.</summary>
|
||||
[Fact]
|
||||
public void Bit_Plus_Array_Rejected()
|
||||
{
|
||||
@@ -231,6 +269,7 @@ public sealed class ModbusAddressParserTests
|
||||
|
||||
// ----- Composition / examples -----
|
||||
|
||||
/// <summary>Verifies parsing of float with word-swap byte ordering.</summary>
|
||||
[Fact]
|
||||
public void Worked_Example_Float_With_Word_Swap()
|
||||
{
|
||||
@@ -242,6 +281,7 @@ public sealed class ModbusAddressParserTests
|
||||
p.ArrayCount.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>Verifies parsing of Int16 array with explicit type code.</summary>
|
||||
[Fact]
|
||||
public void Worked_Example_Int16_Array()
|
||||
{
|
||||
@@ -253,6 +293,7 @@ public sealed class ModbusAddressParserTests
|
||||
p.ByteOrder.ShouldBe(ModbusByteOrder.BigEndian);
|
||||
}
|
||||
|
||||
/// <summary>Verifies parsing of Int32 array using the :I type code.</summary>
|
||||
[Fact]
|
||||
public void Worked_Example_Int32_Array_Via_I_Code()
|
||||
{
|
||||
@@ -264,6 +305,7 @@ public sealed class ModbusAddressParserTests
|
||||
p.ArrayCount.ShouldBe(10);
|
||||
}
|
||||
|
||||
/// <summary>Verifies parsing of float array with word-swap and 6-digit address.</summary>
|
||||
[Fact]
|
||||
public void Worked_Example_Float_Array_Word_Swap_6_Digit()
|
||||
{
|
||||
@@ -275,6 +317,7 @@ public sealed class ModbusAddressParserTests
|
||||
p.ArrayCount.ShouldBe(5);
|
||||
}
|
||||
|
||||
/// <summary>Verifies parsing of string type with specified length.</summary>
|
||||
[Fact]
|
||||
public void Worked_Example_String_With_Length()
|
||||
{
|
||||
@@ -284,6 +327,7 @@ public sealed class ModbusAddressParserTests
|
||||
p.ArrayCount.ShouldBeNull(); // strings ARE multi-register but they are not "array of string"
|
||||
}
|
||||
|
||||
/// <summary>Verifies that TryParse returns a diagnostic on parse failure.</summary>
|
||||
[Fact]
|
||||
public void TryParse_Returns_Diagnostic_On_Failure()
|
||||
{
|
||||
@@ -292,6 +336,7 @@ public sealed class ModbusAddressParserTests
|
||||
err.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that TryParse returns a result on successful parse.</summary>
|
||||
[Fact]
|
||||
public void TryParse_Returns_Result_On_Success()
|
||||
{
|
||||
@@ -304,6 +349,7 @@ public sealed class ModbusAddressParserTests
|
||||
p.ArrayCount.ShouldBe(3);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that addresses with too many colon-separated fields are rejected.</summary>
|
||||
[Fact]
|
||||
public void Too_Many_Colons_Rejected()
|
||||
{
|
||||
|
||||
+20
@@ -14,6 +14,9 @@ public sealed class ModbusFamilyParserTests
|
||||
{
|
||||
// ----- DL205 native: V-memory (octal), Y/C/X/SP coils + discrete -----
|
||||
|
||||
/// <summary>Verifies DL205 user V-memory addresses map to holding registers.</summary>
|
||||
/// <param name="addr">The V-memory address.</param>
|
||||
/// <param name="expectedOffset">The expected holding register offset.</param>
|
||||
[Theory]
|
||||
[InlineData("V0", 0)]
|
||||
[InlineData("V2000", 1024)] // octal 2000 = decimal 1024
|
||||
@@ -30,6 +33,9 @@ public sealed class ModbusFamilyParserTests
|
||||
// plain octal decode — the CPU relocates the system bank to Modbus PDU 0x2100. Octal-decoding
|
||||
// V40400 yields 16640 (0x4100), the WRONG register. Per docs/v2/dl205.md §V-Memory Addressing,
|
||||
// V40400 must map to PDU 0x2100 (decimal 8448) and the bank is contiguous from there.
|
||||
/// <summary>Verifies DL205 system V-memory addresses map to the system bank in holding registers.</summary>
|
||||
/// <param name="addr">The V-memory address.</param>
|
||||
/// <param name="expectedOffset">The expected holding register offset.</param>
|
||||
[Theory]
|
||||
[InlineData("V40400", 0x2100)] // system base
|
||||
[InlineData("V40401", 0x2101)] // next register — contiguous, +1 decimal
|
||||
@@ -42,6 +48,7 @@ public sealed class ModbusFamilyParserTests
|
||||
p.DataType.ShouldBe(ModbusDataType.Int16);
|
||||
}
|
||||
|
||||
/// <summary>Verifies system V-memory helper correctly routes through system bank.</summary>
|
||||
[Fact]
|
||||
public void DL205_SystemVMemory_Helper_Routes_Through_SystemBank()
|
||||
{
|
||||
@@ -52,6 +59,7 @@ public sealed class ModbusFamilyParserTests
|
||||
DirectLogicAddress.UserVMemoryToPdu("V40400").ShouldBe((ushort)16640); // plain octal decode — user-bank only
|
||||
}
|
||||
|
||||
/// <summary>Verifies DL205 Y output addresses map to coils bank.</summary>
|
||||
[Fact]
|
||||
public void DL205_Y_Output_Maps_To_Coils_Bank()
|
||||
{
|
||||
@@ -61,6 +69,7 @@ public sealed class ModbusFamilyParserTests
|
||||
p.DataType.ShouldBe(ModbusDataType.Bool);
|
||||
}
|
||||
|
||||
/// <summary>Verifies DL205 C control relay addresses map to coils bank, not Modicon coil.</summary>
|
||||
[Fact]
|
||||
public void DL205_C_Relay_Maps_To_Coils_Bank_NotModiconCoil()
|
||||
{
|
||||
@@ -71,6 +80,7 @@ public sealed class ModbusFamilyParserTests
|
||||
p.Offset.ShouldBe((ushort)(3072 + 64));
|
||||
}
|
||||
|
||||
/// <summary>Verifies DL205 X input addresses map to discrete inputs.</summary>
|
||||
[Fact]
|
||||
public void DL205_X_Input_Maps_To_DiscreteInputs()
|
||||
{
|
||||
@@ -79,6 +89,7 @@ public sealed class ModbusFamilyParserTests
|
||||
p.Offset.ShouldBe((ushort)15); // octal 17 = decimal 15
|
||||
}
|
||||
|
||||
/// <summary>Verifies DL205 SP special relay addresses map to discrete inputs.</summary>
|
||||
[Fact]
|
||||
public void DL205_SP_Special_Relay_Maps_To_DiscreteInputs()
|
||||
{
|
||||
@@ -87,6 +98,7 @@ public sealed class ModbusFamilyParserTests
|
||||
p.Offset.ShouldBe((ushort)(1024 + 8)); // SpecialBaseDiscrete + octal(10)
|
||||
}
|
||||
|
||||
/// <summary>Verifies DL205 parser falls back to Modicon when native parsing fails.</summary>
|
||||
[Fact]
|
||||
public void DL205_Falls_Back_To_Modicon_When_Native_Misses()
|
||||
{
|
||||
@@ -99,6 +111,7 @@ public sealed class ModbusFamilyParserTests
|
||||
|
||||
// ----- MELSEC native: D / X / Y / M with sub-family-aware X/Y parsing -----
|
||||
|
||||
/// <summary>Verifies MELSEC D register addresses map to holding registers.</summary>
|
||||
[Fact]
|
||||
public void MELSEC_D_Register_Maps_To_HoldingRegisters()
|
||||
{
|
||||
@@ -107,6 +120,7 @@ public sealed class ModbusFamilyParserTests
|
||||
p.Offset.ShouldBe((ushort)100); // base 0 + decimal 100
|
||||
}
|
||||
|
||||
/// <summary>Verifies MELSEC M relay addresses map to coils with decimal format.</summary>
|
||||
[Fact]
|
||||
public void MELSEC_M_Relay_Maps_To_Coils_Decimal()
|
||||
{
|
||||
@@ -115,6 +129,7 @@ public sealed class ModbusFamilyParserTests
|
||||
p.Offset.ShouldBe((ushort)50);
|
||||
}
|
||||
|
||||
/// <summary>Verifies MELSEC Q family treats X addresses as hexadecimal.</summary>
|
||||
[Fact]
|
||||
public void MELSEC_Q_Family_Treats_X20_As_Hex()
|
||||
{
|
||||
@@ -123,6 +138,7 @@ public sealed class ModbusFamilyParserTests
|
||||
p.Offset.ShouldBe((ushort)0x20); // hex 20 = decimal 32
|
||||
}
|
||||
|
||||
/// <summary>Verifies MELSEC F family treats X addresses as octal.</summary>
|
||||
[Fact]
|
||||
public void MELSEC_F_Family_Treats_X20_As_Octal()
|
||||
{
|
||||
@@ -133,6 +149,7 @@ public sealed class ModbusFamilyParserTests
|
||||
|
||||
// ----- Cross-family safety / Generic regression -----
|
||||
|
||||
/// <summary>Verifies Generic family does not try DL205 native parsing.</summary>
|
||||
[Fact]
|
||||
public void Generic_Family_Does_Not_Try_DL205_Branch()
|
||||
{
|
||||
@@ -143,6 +160,7 @@ public sealed class ModbusFamilyParserTests
|
||||
error.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
/// <summary>Verifies C100 under Generic family means Modicon coil 99.</summary>
|
||||
[Fact]
|
||||
public void C100_Under_Generic_Means_Modicon_Coil_99()
|
||||
{
|
||||
@@ -153,6 +171,7 @@ public sealed class ModbusFamilyParserTests
|
||||
p.Offset.ShouldBe((ushort)99);
|
||||
}
|
||||
|
||||
/// <summary>Verifies suffix grammar composes correctly with native addresses.</summary>
|
||||
[Fact]
|
||||
public void Suffix_Grammar_Composes_With_Native_Address()
|
||||
{
|
||||
@@ -165,6 +184,7 @@ public sealed class ModbusFamilyParserTests
|
||||
p.ArrayCount.ShouldBe(5);
|
||||
}
|
||||
|
||||
/// <summary>Verifies DL205 bit suffix works on V-memory addresses.</summary>
|
||||
[Fact]
|
||||
public void DL205_Bit_Suffix_On_VMemory()
|
||||
{
|
||||
|
||||
+11
@@ -7,6 +7,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.Addressing.Tests;
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class ModbusModiconAddressTests
|
||||
{
|
||||
/// <summary>Verifies that valid Modicon addresses parse correctly to region and offset.</summary>
|
||||
/// <param name="address">The Modicon address string to parse.</param>
|
||||
/// <param name="expectedRegion">The expected Modbus region.</param>
|
||||
/// <param name="expectedOffset">The expected PDU offset.</param>
|
||||
[Theory]
|
||||
// 5-digit form, one per region. Trailing 4 digits = 1-based register; PDU offset is one less.
|
||||
[InlineData("00001", ModbusRegion.Coils, (ushort)0)]
|
||||
@@ -31,6 +35,9 @@ public sealed class ModbusModiconAddressTests
|
||||
offset.ShouldBe(expectedOffset);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that invalid addresses produce diagnostic error messages.</summary>
|
||||
/// <param name="address">The invalid address string to test.</param>
|
||||
/// <param name="fragment">The expected diagnostic fragment in the exception message.</param>
|
||||
[Theory]
|
||||
[InlineData("", "null or empty")]
|
||||
[InlineData(" ", "null or empty")]
|
||||
@@ -49,6 +56,7 @@ public sealed class ModbusModiconAddressTests
|
||||
.Message.ShouldContain(fragment, Case.Insensitive);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that TryParse returns false with a diagnostic error on invalid input.</summary>
|
||||
[Fact]
|
||||
public void TryParse_Returns_False_With_Diagnostic_On_Invalid()
|
||||
{
|
||||
@@ -57,6 +65,7 @@ public sealed class ModbusModiconAddressTests
|
||||
error.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that TryParse returns true with null error on valid input.</summary>
|
||||
[Fact]
|
||||
public void TryParse_Returns_True_With_Null_Error_On_Valid()
|
||||
{
|
||||
@@ -67,6 +76,7 @@ public sealed class ModbusModiconAddressTests
|
||||
error.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that TryParse handles null input gracefully.</summary>
|
||||
[Fact]
|
||||
public void TryParse_Handles_Null()
|
||||
{
|
||||
@@ -74,6 +84,7 @@ public sealed class ModbusModiconAddressTests
|
||||
error.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that TryParse trims leading and trailing whitespace.</summary>
|
||||
[Fact]
|
||||
public void TryParse_Trims_Whitespace()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user