review(Driver.TwinCAT.Cli): clean parse errors + FlushLogging() in finally

Re-review at 7286d320. -008 (Low): ParseValue maps FormatException/OverflowException to a
clean CommandException (was raw stack trace) + tests. -009: FlushLogging() in all 5 commands'
finally blocks (parity with AbCip.Cli).
This commit is contained in:
Joseph Doherty
2026-06-19 12:08:45 -04:00
parent f8bf067243
commit 7580e37807
7 changed files with 154 additions and 26 deletions
+84 -2
View File
@@ -4,8 +4,8 @@
|---|---|
| Module | `src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli` |
| Reviewer | Claude Code |
| Review date | 2026-05-22 |
| Commit reviewed | `76d35d1` |
| Review date | 2026-06-19 |
| Commit reviewed | `111d6983` |
| Status | Reviewed |
| Open findings | 0 |
@@ -251,3 +251,85 @@ accessor required by the abstract base property is intentionally a no-op, and th
backing field would cause the two to drift on every refactor. The inner-block comment was
tightened to point at the XML summary so the design intent survives whichever doc surface a
future maintainer reads first.
---
## Re-review 2026-06-19 (commit 111d6983)
All seven prior findings remain Resolved. The re-review covers the same 8 source files and 4
test files at the current HEAD. No new findings were added to categories 2, 3, 5, 6, 7, 8;
two new Low findings were identified in categories 4 and 1/4, both fixed in-session.
#### Checklist coverage (re-review)
| # | Category | Result |
|---|---|---|
| 1 | Correctness & logic bugs | No new issues found |
| 2 | OtOpcUa conventions | No new issues found |
| 3 | Concurrency & thread safety | No new issues found |
| 4 | Error handling & resilience | Driver.TwinCAT.Cli-008, Driver.TwinCAT.Cli-009 |
| 5 | Security | No new issues found |
| 6 | Performance & resource management | No new issues found |
| 7 | Design-document adherence | No new issues found |
| 8 | Code organization & conventions | No new issues found |
| 9 | Testing coverage | No new issues found |
| 10 | Documentation & comments | No new issues found |
### Driver.TwinCAT.Cli-008
| Field | Value |
|---|---|
| Severity | Low |
| Category | Error handling & resilience |
| Location | `Commands/WriteCommand.cs:73-93` |
| Status | Resolved |
**Description:** `ParseValue` delegates to the BCL numeric parse methods (`int.Parse`,
`sbyte.Parse`, etc.) which throw raw `FormatException` or `OverflowException` on bad
input. Those exceptions propagate all the way through `ExecuteAsync` and are caught by
CliFx's top-level handler, but CliFx prints a full stack trace for unrecognised exception
types. An operator passing `--value xyz --type DInt` sees several lines of .NET internals
instead of the single-line "Cannot parse 'xyz' as DInt" message that `CommandException` would
produce. The existing test `ParseValue_non_numeric_for_numeric_types_throws` confirmed and
preserved the broken behaviour by asserting `FormatException`, so the gap survived review.
**Recommendation:** Wrap the switch body in a `try/catch` that re-throws `CommandException`
(which is already thrown by `ParseBool`) and maps `FormatException`/`OverflowException` to a
`CommandException` carrying the raw value and type name.
**Resolution:** Wrapped the `ParseValue` switch in `try/catch`; `FormatException` and
`OverflowException` are caught (via `when` pattern) and re-thrown as `CommandException`
with `"Cannot parse '{raw}' as {type}: {message}"`. Pre-existing `CommandException` from
`ParseBool` / the unsupported-type arm / the `Structure` guard are re-thrown unchanged.
Updated the test: renamed `ParseValue_non_numeric_for_numeric_types_throws`
`ParseValue_non_numeric_for_numeric_types_throws_CommandException` and asserted the message
contains both the value and type; added a second test
`ParseValue_overflow_for_numeric_types_throws_CommandException` (300 as SInt). 70 tests
pass (was 69). Date: 2026-06-19. SHA: blank (not committed).
### Driver.TwinCAT.Cli-009
| Field | Value |
|---|---|
| Severity | Low |
| Category | Error handling & resilience |
| Location | `Commands/BrowseCommand.cs:62-65`, `Commands/ProbeCommand.cs:59-62`, `Commands/ReadCommand.cs:50-53`, `Commands/WriteCommand.cs:63-67`, `Commands/SubscribeCommand.cs:103-111` |
| Status | Resolved |
**Description:** `DriverCommandBase` documents that `FlushLogging()` must be called in a
`finally` block to flush any buffered Serilog output before the process exits (see its XML
`<summary>` on `FlushLogging`). None of the five TwinCAT CLI commands called it. The sibling
`AbCip.Cli` correctly calls `FlushLogging()` in each command's `finally`; TwinCAT, Modbus,
S7, AbLegacy, and FOCAS CLIs all omit it. The impact is that any `--verbose` debug lines
buffered by Serilog's default asynchronous sink may be lost on process exit — a diagnostic
CLI that drops diagnostic output on crash is self-defeating.
**Recommendation:** Add `FlushLogging()` as the last statement in every command's outer
`finally` block, after `ShutdownAsync` (mirroring the AbCip pattern).
**Resolution:** Added `FlushLogging()` as the last call in the `finally` block of every
`ExecuteAsync` in `BrowseCommand`, `ProbeCommand`, `ReadCommand`, `WriteCommand`, and
`SubscribeCommand`. No test change needed (the call is not unit-testable without a real ADS
router and a real Serilog sink; the fix is verified structurally at build time). The systemic
omission in the Modbus/S7/AbLegacy/FOCAS sibling CLIs is out of this module's scope and left
for their respective re-reviews. Date: 2026-06-19. SHA: blank (not committed).