Codex introduction. Base process tidy.

This commit is contained in:
Andy Howard
2026-09-10 09:35:05 +01:00
parent c2020c8302
commit c2a8757ebf
10 changed files with 466 additions and 63 deletions
@@ -0,0 +1,170 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Procs;
using Sarand.Net.Standard.Base.Tasks;
using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace Sacs.UnitTests.Net.Standard.Base.Procs
{
//=======================================================================
[TestClass]
public class SaProcess_Base_Tester
{
//-------------------------------------------------------------------
private class TestProcessConfig : SaProcess_Base.Config
{
public TestProcessConfig()
{
AllowInterruption = true;
ConfirmInterruption = false;
}
}
//-------------------------------------------------------------------
private class TestProcess : SaProcess_Base
{
public TestProcess(bool stopOnCancellation) : base(new TestProcessConfig())
{
StopOnCancellation = stopOnCancellation;
}
public override String ProcessDescription { get { return "Test process"; } }
public ManualResetEventSlim Started { get; } = new ManualResetEventSlim(false);
public ManualResetEventSlim Release { get; } = new ManualResetEventSlim(false);
public bool CancellationObserved { get; private set; }
private bool StopOnCancellation { get; }
protected override bool Run()
{
Started.Set();
while (!Release.Wait(5))
{
if (StopOnCancellation && IsInterrupted)
{
CancellationObserved = true;
return false;
}
}
return true;
}
}
//-------------------------------------------------------------------
[TestMethod]
public async Task RunAsync_CompletesAndClearsAsyncState()
{
using (TestProcess process = new TestProcess(false))
{
Task runTask = process.RunAsync();
Assert.IsTrue(process.Started.Wait(1000), "Process did not start.");
Assert.IsTrue(process.IsAsync);
process.Release.Set();
await runTask;
Assert.IsFalse(process.IsAsync);
Assert.IsFalse(process.Processing);
Assert.IsTrue(process.ProcessEnded);
}
}
//-------------------------------------------------------------------
[TestMethod]
public async Task RunAsync_RejectsOverlappingRun()
{
using (TestProcess process = new TestProcess(false))
{
Task runTask = process.RunAsync();
Assert.IsTrue(process.Started.Wait(1000), "Process did not start.");
await Assert.ThrowsExceptionAsync<SaProcess_Base.InProgressException>(() => process.RunAsync());
process.Release.Set();
await runTask;
}
}
//-------------------------------------------------------------------
[TestMethod]
public async Task StopProcessAsync_TimeoutRetainsAsyncState()
{
using (TestProcess process = new TestProcess(false))
{
Task runTask = process.RunAsync();
Assert.IsTrue(process.Started.Wait(1000), "Process did not start.");
Assert.IsFalse(await process.StopProcessAsync(40));
Assert.IsTrue(process.IsAsync, "A timed-out stop must retain the active process state.");
await Assert.ThrowsExceptionAsync<SaProcess_Base.InProgressException>(() => process.RunAsync());
process.Release.Set();
await runTask;
Assert.IsFalse(process.IsAsync);
}
}
//-------------------------------------------------------------------
[TestMethod]
public async Task StopProcessAsync_CooperativeCancellationStopsProcess()
{
using (TestProcess process = new TestProcess(true))
{
Task runTask = process.RunAsync();
Assert.IsTrue(process.Started.Wait(1000), "Process did not start.");
Assert.IsTrue(await process.StopProcessAsync(1000));
await runTask;
Assert.IsTrue(process.CancellationObserved);
Assert.IsFalse(process.IsAsync);
Assert.IsTrue(await process.StopProcessAsync(20), "Stopping an idle process should succeed.");
}
}
//-------------------------------------------------------------------
[TestMethod]
public async Task StopProcessAsync_CancellationCallbackCanReadAsyncState()
{
using (TestProcess process = new TestProcess(true))
{
Task runTask = process.RunAsync();
Assert.IsTrue(process.Started.Wait(1000), "Process did not start.");
CancellationToken cancellationToken = GetCancellationToken(process);
bool callbackInvoked = false;
using (cancellationToken.Register(() =>
{
callbackInvoked = true;
Assert.IsTrue(process.IsAsync);
}))
{
Task<bool> stopTask = process.StopProcessAsync(1000);
Task completedTask = await Task.WhenAny(stopTask, Task.Delay(2000));
Assert.AreSame(stopTask, completedTask, "Cancellation callback deadlocked on the lifecycle lock.");
Assert.IsTrue(await stopTask);
}
await runTask;
Assert.IsTrue(callbackInvoked);
}
}
//-------------------------------------------------------------------
private static CancellationToken GetCancellationToken(SaProcess_Base process)
{
PropertyInfo controlProperty = typeof(SaProcess_Base).GetProperty("Control", BindingFlags.Instance | BindingFlags.NonPublic);
Assert.IsNotNull(controlProperty);
Object control = controlProperty.GetValue(process);
Assert.IsNotNull(control);
PropertyInfo taskDataProperty = control.GetType().GetProperty("TaskData", BindingFlags.Instance | BindingFlags.Public);
Assert.IsNotNull(taskDataProperty);
SacsTaskData taskData = (SacsTaskData)taskDataProperty.GetValue(control);
Assert.IsNotNull(taskData);
return taskData.CancellationToken;
}
}
//=======================================================================
}