diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionWorkerClientFactoryFakeWorkerTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionWorkerClientFactoryFakeWorkerTests.cs index a61cb96..11b09d5 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionWorkerClientFactoryFakeWorkerTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionWorkerClientFactoryFakeWorkerTests.cs @@ -7,6 +7,7 @@ using ZB.MOM.WW.MxGateway.Server.Metrics; using ZB.MOM.WW.MxGateway.Server.Sessions; using ZB.MOM.WW.MxGateway.Server.Workers; using ZB.MOM.WW.MxGateway.Tests.Gateway.Workers.Fakes; +using ZB.MOM.WW.MxGateway.Tests.TestSupport; namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Sessions; @@ -330,62 +331,4 @@ public sealed class SessionWorkerClientFactoryFakeWorkerTests : IAsyncDisposable DateTimeOffset.UtcNow); } - /// - /// Fake worker process for testing process lifecycle. - /// awaits a completed only by - /// or , so a caller observing - /// completion can trust that exit actually happened — bringing this fake into - /// parity with the smoke-test variant in GatewayEndToEndFakeWorkerSmokeTests - /// (Tests-015 / Tests-023). This removes the latent regression vector where a - /// future Assert.True(launcher.Process.HasExited) in this file would - /// pass spuriously regardless of whether the worker truly exited. - /// - private sealed class FakeWorkerProcess(int processId) : IWorkerProcess - { - private readonly TaskCompletionSource _exited = new(TaskCreationOptions.RunContinuationsAsynchronously); - private bool _disposed; - - /// - public int Id { get; } = processId; - - /// Gets a value indicating whether the process has exited. - public bool HasExited { get; private set; } - - /// Gets the process exit code, or null if the process has not exited. - public int? ExitCode { get; private set; } - - /// Gets the number of times the Kill method was called. - public int KillCount { get; private set; } - - /// - public ValueTask WaitForExitAsync(CancellationToken cancellationToken) - { - return new ValueTask(_exited.Task.WaitAsync(cancellationToken)); - } - - /// - public void Kill(bool entireProcessTree) - { - KillCount++; - MarkExited(-1); - } - - /// - public void Dispose() - { - _disposed = true; - } - - /// Gets a value indicating whether this process has been disposed. - public bool IsDisposed => _disposed; - - /// Marks the process as exited with the specified exit code. - /// The process exit code. - public void MarkExited(int exitCode) - { - HasExited = true; - ExitCode = exitCode; - _exited.TrySetResult(); - } - } } diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerClientTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerClientTests.cs index 7d46680..9750c14 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerClientTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerClientTests.cs @@ -158,7 +158,7 @@ public sealed class WorkerClientTests public async Task ReadLoop_WhenClientFaults_KillsOwnedWorkerProcess() { await using PipePair pipePair = await PipePair.CreateAsync(); - FakeWorkerProcess process = new(); + FakeWorkerProcess process = new(WorkerProcessId); await using WorkerClient client = CreateClient( pipePair, new WorkerClientOptions @@ -309,7 +309,7 @@ public sealed class WorkerClientTests public async Task DisposeAsync_WhenOwnedWorkerStillRuns_KillsProcessBeforeDisposing() { await using PipePair pipePair = await PipePair.CreateAsync(); - FakeWorkerProcess process = new(); + FakeWorkerProcess process = new(WorkerProcessId); WorkerClient client = CreateClient(pipePair, processHandle: CreateProcessHandle(process)); await client.DisposeAsync().AsTask().WaitAsync(TestTimeout); @@ -764,49 +764,4 @@ public sealed class WorkerClientTests } } - private sealed class FakeWorkerProcess : IWorkerProcess - { - private readonly TaskCompletionSource _exited = new(TaskCreationOptions.RunContinuationsAsynchronously); - - /// Gets the process ID. - public int Id { get; } = WorkerProcessId; - - /// Gets a value indicating whether the process has exited. - public bool HasExited { get; private set; } - - /// Gets the process exit code. - public int? ExitCode { get; private set; } - - /// Gets the number of times kill was called. - public int KillCount { get; private set; } - - /// Gets the last kill request's entire process tree flag. - public bool KillEntireProcessTree { get; private set; } - - /// Gets a value indicating whether dispose was called. - public bool Disposed { get; private set; } - - /// - public ValueTask WaitForExitAsync(CancellationToken cancellationToken) - { - return new ValueTask(_exited.Task.WaitAsync(cancellationToken)); - } - - /// Records a kill request. - /// Whether to kill the entire process tree. - public void Kill(bool entireProcessTree) - { - KillCount++; - KillEntireProcessTree = entireProcessTree; - HasExited = true; - ExitCode = -1; - _exited.TrySetResult(); - } - - /// - public void Dispose() - { - Disposed = true; - } - } } diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerProcessLauncherTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerProcessLauncherTests.cs index f8f93a5..adeacf1 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerProcessLauncherTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerProcessLauncherTests.cs @@ -4,6 +4,7 @@ using ZB.MOM.WW.MxGateway.Contracts; using ZB.MOM.WW.MxGateway.Server.Configuration; using ZB.MOM.WW.MxGateway.Server.Metrics; using ZB.MOM.WW.MxGateway.Server.Workers; +using ZB.MOM.WW.MxGateway.Tests.TestSupport; namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Workers; @@ -240,45 +241,6 @@ public sealed class WorkerProcessLauncherTests } } - /// Fake worker process for testing process lifecycle and exit behavior. - private sealed class FakeWorkerProcess(int processId) : IWorkerProcess - { - /// - public int Id { get; } = processId; - - /// Gets or sets a value indicating whether the process has exited. - public bool HasExited { get; set; } - - /// Gets or sets the process exit code. - public int? ExitCode { get; set; } - - /// Gets a value indicating whether the Dispose method was called. - public bool DisposeCalled { get; private set; } - - /// Gets a value indicating whether the Kill method was called. - public bool KillCalled { get; private set; } - - /// - public ValueTask WaitForExitAsync(CancellationToken cancellationToken) - { - return ValueTask.CompletedTask; - } - - /// - public void Kill(bool entireProcessTree) - { - Assert.True(entireProcessTree); - KillCalled = true; - HasExited = true; - } - - /// - public void Dispose() - { - DisposeCalled = true; - } - } - /// Fake startup probe that immediately succeeds. private sealed class SucceedingStartupProbe : IWorkerStartupProbe { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/TestSupport/FakeWorkerProcess.cs b/src/ZB.MOM.WW.MxGateway.Tests/TestSupport/FakeWorkerProcess.cs index f65897f..2bb0386 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/TestSupport/FakeWorkerProcess.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/TestSupport/FakeWorkerProcess.cs @@ -4,10 +4,21 @@ namespace ZB.MOM.WW.MxGateway.Tests.TestSupport; /// /// Lightweight in-process stand-in for used by fake worker -/// launchers in end-to-end tests. Call from the fake worker -/// body once the shutdown-ack handshake is complete so that callers awaiting -/// observe real exit rather than a timeout. +/// launchers and lifecycle tests. /// +/// +/// +/// awaits a that is +/// completed only by or , so a caller observing +/// completion can trust that exit actually happened rather than passing spuriously. +/// +/// +/// The disposal and kill bookkeeping is exposed under several aliases +/// (//; +/// /) so the various lifecycle tests can +/// keep their existing assertion vocabulary while sharing one definition. +/// +/// public sealed class FakeWorkerProcess(int processId) : IWorkerProcess { private readonly TaskCompletionSource _exited = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -15,23 +26,44 @@ public sealed class FakeWorkerProcess(int processId) : IWorkerProcess /// Gets the process identifier. public int Id { get; } = processId; - /// Gets a value indicating whether the process has exited. - public bool HasExited { get; private set; } + /// Gets or sets a value indicating whether the process has exited. + public bool HasExited { get; set; } - /// Gets the exit code of the process, or if it has not exited. - public int? ExitCode { get; private set; } + /// Gets or sets the exit code of the process, or if it has not exited. + public int? ExitCode { get; set; } + + /// Gets the number of times was called. + public int KillCount { get; private set; } + + /// Gets a value indicating whether was called at least once. + public bool KillCalled => KillCount > 0; + + /// Gets the entireProcessTree flag from the most recent call. + public bool KillEntireProcessTree { get; private set; } + + /// Gets a value indicating whether was called. + public bool IsDisposed { get; private set; } + + /// Gets a value indicating whether was called. + public bool DisposeCalled => IsDisposed; + + /// Gets a value indicating whether was called. + public bool Disposed => IsDisposed; /// public ValueTask WaitForExitAsync(CancellationToken cancellationToken) => new(_exited.Task.WaitAsync(cancellationToken)); /// - public void Kill(bool entireProcessTree) => MarkExited(-1); + public void Kill(bool entireProcessTree) + { + KillCount++; + KillEntireProcessTree = entireProcessTree; + MarkExited(-1); + } /// - public void Dispose() - { - } + public void Dispose() => IsDisposed = true; /// /// Marks the process as exited with the specified exit code and unblocks