fix(driver-ablegacy): resolve Medium code-review finding (Driver.AbLegacy-003)

TryParse now rejects three classes of malformed PCCC address:
- Sub-element + bit-index together (e.g. T4:0.ACC/2) — never valid in PCCC
- File number on I/O/S system files (e.g. I3:0, S2:1) — single-letter only
- Sub-element on non-T/C/R files (e.g. B3:0.DN, N7:0.FOO) — only Timer,
  Counter, and Control files carry structured elements

New helper predicates IsNoFileNumberLetter / IsSubElementFileLetter
keep the parser's intent clear. Regression tests added in AbLegacyAddressTests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-22 09:23:35 -04:00
parent c8a237e5e6
commit 7d30009dc8
2 changed files with 64 additions and 0 deletions

View File

@@ -65,4 +65,41 @@ public sealed class AbLegacyAddressTests
a.ShouldNotBeNull();
a.ToLibplctagName().ShouldBe(input);
}
// ---- Driver.AbLegacy-003: Parser tightening ----
[Theory]
[InlineData("T4:0.ACC/2")] // sub-element + bit index — never valid in PCCC
[InlineData("C5:0.PRE/3")]
public void TryParse_rejects_subelement_plus_bitindex(string input) =>
AbLegacyAddress.TryParse(input).ShouldBeNull();
[Theory]
[InlineData("I3:0")] // I is a system file — no file number allowed
[InlineData("O2:1")]
[InlineData("S2:1")]
public void TryParse_rejects_file_number_on_IOS_files(string input) =>
AbLegacyAddress.TryParse(input).ShouldBeNull();
[Theory]
[InlineData("B3:0.DN")] // B (bit) file has no structured elements
[InlineData("N7:0.FOO")] // N (integer) file has no structured elements
[InlineData("F8:0.ACC")] // F (float) file has no structured elements
[InlineData("L9:0.PRE")] // L (long) file has no structured elements
public void TryParse_rejects_subelement_on_non_structured_file(string input) =>
AbLegacyAddress.TryParse(input).ShouldBeNull();
[Theory]
[InlineData("T4:0.ACC")] // T, C, R are the only structured-element files
[InlineData("C5:0.PRE")]
[InlineData("R6:0.LEN")]
public void TryParse_accepts_subelement_only_on_TCR_files(string input) =>
AbLegacyAddress.TryParse(input).ShouldNotBeNull();
[Theory]
[InlineData("I:0/0")] // I/O/S without file number are valid
[InlineData("O:1/2")]
[InlineData("S:1")]
public void TryParse_accepts_IOS_without_file_number(string input) =>
AbLegacyAddress.TryParse(input).ShouldNotBeNull();
}