using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Driver.S7.Cli.Tests; /// /// Driver.S7.Cli-004: every S7 CLI command must own one disposal mechanism for the /// S7Driver, not two. The chosen mechanism is await using var driver = ... /// — S7Driver.DisposeAsync already calls ShutdownAsync, so an additional /// explicit driver.ShutdownAsync(...) in a finally block runs shutdown /// twice (three times on subscribe). These tests guard against that regression by /// scanning the command source files. /// [Trait("Category", "Unit")] public sealed class CommandDisposalConventionsTests { private static readonly string CommandsDir = LocateCommandsDir(); [Theory] [InlineData("ProbeCommand.cs")] [InlineData("ReadCommand.cs")] [InlineData("WriteCommand.cs")] [InlineData("SubscribeCommand.cs")] public void Command_does_not_call_ShutdownAsync_explicitly(string commandFile) { var path = Path.Combine(CommandsDir, commandFile); File.Exists(path).ShouldBeTrue($"Expected {path} to exist."); var source = File.ReadAllText(path); // The await-using statement is the single disposal mechanism. An explicit // driver.ShutdownAsync(...) call (typically inside a finally block) re-invokes // a shutdown path that DisposeAsync already runs and is the smell -004 flags. source.ShouldNotContain("driver.ShutdownAsync("); } [Theory] [InlineData("ProbeCommand.cs")] [InlineData("ReadCommand.cs")] [InlineData("WriteCommand.cs")] [InlineData("SubscribeCommand.cs")] public void Command_uses_await_using_for_S7Driver(string commandFile) { var path = Path.Combine(CommandsDir, commandFile); var source = File.ReadAllText(path); source.ShouldContain("await using var driver = new S7Driver("); } private static string LocateCommandsDir() { // Walk up from the test assembly bin/ folder to the repo root, then into the // source project's Commands/ directory. The test-host puts CWD somewhere under // bin/Debug/net10.0 so we resolve relative to AppContext.BaseDirectory. var dir = new DirectoryInfo(AppContext.BaseDirectory); while (dir is not null && !File.Exists(Path.Combine(dir.FullName, "ZB.MOM.WW.OtOpcUa.slnx"))) dir = dir.Parent; dir.ShouldNotBeNull("Could not find solution root (ZB.MOM.WW.OtOpcUa.slnx)."); return Path.Combine( dir!.FullName, "src", "Drivers", "Cli", "ZB.MOM.WW.OtOpcUa.Driver.S7.Cli", "Commands"); } }