review(Driver.AbLegacy.Cli): add FlushLogging() to command finally blocks
Re-review at 7286d320. -008 (Low): all four commands now FlushLogging() in finally (parity
with AbCip.Cli; subscribe could drop shutdown log lines) + IL-inspection test.
This commit is contained in:
+68
-2
@@ -4,8 +4,7 @@ using CliFx.Attributes;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Cli.Commands;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Cli.Tests;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Cli.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Locks in the CLI command-option contract surface area — short aliases and
|
||||
@@ -83,4 +82,71 @@ public sealed class CommandMetadataTests
|
||||
attr.Description.ShouldNotBeNull();
|
||||
attr.Description!.ShouldContain("250", Case.Insensitive);
|
||||
}
|
||||
|
||||
// ---------- Driver.AbLegacy.Cli-008 — all commands must call FlushLogging() in their finally ----------
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that every AbLegacy CLI command's async state-machine references
|
||||
/// <see cref="DriverCommandBase.FlushLogging"/> so Serilog output emitted during
|
||||
/// driver shutdown is flushed before process exit (regression for
|
||||
/// Driver.AbLegacy.Cli-008, matching the fix applied to AbCip CLI Driver.AbCip.Cli-005).
|
||||
/// </summary>
|
||||
/// <param name="commandType">The command type whose state machine is inspected.</param>
|
||||
[Theory]
|
||||
[InlineData(typeof(ProbeCommand))]
|
||||
[InlineData(typeof(ReadCommand))]
|
||||
[InlineData(typeof(WriteCommand))]
|
||||
[InlineData(typeof(SubscribeCommand))]
|
||||
public void ExecuteAsync_calls_FlushLogging_in_state_machine(System.Type commandType)
|
||||
{
|
||||
// C# async methods are compiled into a nested '<ExecuteAsync>d__N' state machine
|
||||
// class with a MoveNext() method that contains the actual IL. We scan the
|
||||
// MoveNext() body for a call token that resolves to DriverCommandBase.FlushLogging
|
||||
// using the module's metadata.
|
||||
var flushMethod = typeof(DriverCommandBase)
|
||||
.GetMethod("FlushLogging",
|
||||
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy);
|
||||
flushMethod.ShouldNotBeNull("DriverCommandBase.FlushLogging must exist");
|
||||
|
||||
// Locate the compiler-generated state-machine nested type for ExecuteAsync.
|
||||
var stateMachine = commandType
|
||||
.GetNestedTypes(BindingFlags.NonPublic)
|
||||
.FirstOrDefault(t => t.Name.Contains("ExecuteAsync"));
|
||||
stateMachine.ShouldNotBeNull(
|
||||
$"{commandType.Name} must have a compiler-generated ExecuteAsync state machine");
|
||||
|
||||
var moveNext = stateMachine!
|
||||
.GetMethod("MoveNext", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
moveNext.ShouldNotBeNull("MoveNext must exist on the state machine");
|
||||
|
||||
var body = moveNext!.GetMethodBody();
|
||||
body.ShouldNotBeNull("MoveNext must have an inspectable method body");
|
||||
|
||||
// Walk the IL stream: call/callvirt opcodes (0x28 / 0x6F) are followed by a
|
||||
// 4-byte metadata token. Check whether any token resolves to FlushLogging.
|
||||
var il = body!.GetILAsByteArray();
|
||||
il.ShouldNotBeNull();
|
||||
var module = moveNext.Module;
|
||||
bool found = false;
|
||||
for (int i = 0; i < il!.Length - 4; i++)
|
||||
{
|
||||
if (il[i] != 0x28 && il[i] != 0x6F) continue; // call / callvirt
|
||||
int token = il[i + 1] | (il[i + 2] << 8) | (il[i + 3] << 16) | (il[i + 4] << 24);
|
||||
try
|
||||
{
|
||||
var resolved = module.ResolveMethod(token);
|
||||
if (resolved?.Name == "FlushLogging" &&
|
||||
resolved.DeclaringType == typeof(DriverCommandBase))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (ArgumentException) { /* token is a field or type, not a method */ }
|
||||
}
|
||||
|
||||
found.ShouldBeTrue(
|
||||
$"{commandType.Name}.ExecuteAsync must call FlushLogging() in its finally block " +
|
||||
"so Serilog output during driver shutdown is not lost (Driver.AbLegacy.Cli-008).");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user