using System; using System.Threading; using System.Threading.Tasks; using Shouldly; using Xunit; using ZB.MOM.WW.LmxOpcUa.Host.MxAccess; namespace ZB.MOM.WW.LmxOpcUa.Tests.MxAccess { /// /// Verifies the single-threaded apartment worker used to marshal COM calls for the MXAccess bridge. /// public class StaComThreadTests : IDisposable { private readonly StaComThread _thread; /// /// Starts a fresh STA thread instance for each test. /// public StaComThreadTests() { _thread = new StaComThread(); _thread.Start(); } /// /// Disposes the STA thread after each test. /// public void Dispose() => _thread.Dispose(); /// /// Confirms that queued work runs on a thread configured for STA apartment state. /// [Fact] public async Task RunAsync_ExecutesOnStaThread() { var apartmentState = await _thread.RunAsync(() => Thread.CurrentThread.GetApartmentState()); apartmentState.ShouldBe(ApartmentState.STA); } /// /// Confirms that action delegates run to completion on the STA thread. /// [Fact] public async Task RunAsync_Action_Completes() { var executed = false; await _thread.RunAsync(() => executed = true); executed.ShouldBe(true); } /// /// Confirms that function delegates can return results from the STA thread. /// [Fact] public async Task RunAsync_Func_ReturnsResult() { var result = await _thread.RunAsync(() => 42); result.ShouldBe(42); } /// /// Confirms that exceptions thrown on the STA thread propagate back to the caller. /// [Fact] public async Task RunAsync_PropagatesException() { await Should.ThrowAsync( _thread.RunAsync(() => throw new InvalidOperationException("test error"))); } /// /// Confirms that disposing the STA thread stops it from accepting additional work. /// [Fact] public void Dispose_Stops_Thread() { var thread = new StaComThread(); thread.Start(); thread.IsRunning.ShouldBe(true); thread.Dispose(); // After dispose, should not accept new work Should.Throw(() => thread.RunAsync(() => { }).GetAwaiter().GetResult()); } /// /// Confirms that multiple queued work items all execute successfully on the STA thread. /// [Fact] public async Task MultipleWorkItems_ExecuteInOrder() { var results = new System.Collections.Concurrent.ConcurrentBag(); await Task.WhenAll( _thread.RunAsync(() => results.Add(1)), _thread.RunAsync(() => results.Add(2)), _thread.RunAsync(() => results.Add(3))); results.Count.ShouldBe(3); } } }