diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipTransportSwitchTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipTransportSwitchTests.cs index 6fb00abb..0ab8c14f 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipTransportSwitchTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipTransportSwitchTests.cs @@ -116,6 +116,21 @@ public sealed class AbCipTransportSwitchTests driver.TagFactory.ShouldBeOfType(); } + /// + /// Positive control for the libplctag path against a listener that accepts TCP and never speaks CIP. + /// libplctag reports the stall two ways that race on the wall clock: the probe's own cancellation + /// ("Probe timed out after Ns") or libplctag's internal timeout, which has the same duration and + /// surfaces as the ErrorTimeout status. Which fires first depends on scheduler load, so both are + /// the libplctag outcome; the native path never produces either (it fails at once with "Connection lost"). + /// + private static void ShouldBeLibplctagTimeout(string? message) + { + message.ShouldNotBeNull(); + (message.Contains("timed out", StringComparison.OrdinalIgnoreCase) + || message.Contains("ErrorTimeout", StringComparison.Ordinal)) + .ShouldBeTrue($"expected the libplctag stall outcome (timed out / ErrorTimeout) but was: {message}"); + } + /// /// The probe's Phase 1 TCP preflight rejects a refused port before any runtime exists, so the native /// path is proven against a listener that accepts TCP and hangs up: only a runtime that really opens @@ -150,7 +165,7 @@ public sealed class AbCipTransportSwitchTests lib.Message!.ShouldNotContain("Connection lost"); // Positive (#574): the libplctag path ran and reported its own outcome (the bounded probe timeout), // not merely "something other than the native text". - lib.Message!.ShouldContain("timed out"); + ShouldBeLibplctagTimeout(lib.Message); } /// @@ -188,7 +203,7 @@ public sealed class AbCipTransportSwitchTests lib.Message!.ShouldNotContain("Connection lost"); // Positive (#574): the libplctag path ran and reported its own outcome (the bounded probe timeout), // not merely "something other than the native text". - lib.Message!.ShouldContain("timed out"); + ShouldBeLibplctagTimeout(lib.Message); } /// The probe deserialises with its own string-enum converter; a bad transport is a config error. @@ -268,7 +283,7 @@ public sealed class AbCipTransportSwitchTests unpinned.Message!.ShouldContain("CIP handshake failed: Connection lost"); pinned.Ok.ShouldBeFalse(); pinned.Message!.ShouldNotContain("Connection lost"); - pinned.Message!.ShouldContain("timed out"); // positive: libplctag's own outcome (#574) + ShouldBeLibplctagTimeout(pinned.Message); // positive: libplctag's own outcome (#574) } /// Gitea #572: browsePageDelayMs parses (default 5 ms, 0 allowed, negative clamps to 0).