Files

170 lines
5.9 KiB
C#

using System.Collections.Concurrent;
using MBN_STOCK_WEBVIEW.Playout.Runtime;
namespace MBN_STOCK_WEBVIEW.Playout.Tests;
public sealed class StaDispatcherTests
{
[Fact]
public async Task InvokeAsync_RunsCallbacksOnOneStaThreadInFifoOrder()
{
await using var dispatcher = new StaDispatcher(capacity: 4);
using var firstStarted = new ManualResetEventSlim();
using var releaseFirst = new ManualResetEventSlim();
var calls = new ConcurrentQueue<(int Order, int ThreadId, ApartmentState Apartment)>();
var first = dispatcher.InvokeAsync(
() =>
{
calls.Enqueue((
1,
Environment.CurrentManagedThreadId,
Thread.CurrentThread.GetApartmentState()));
firstStarted.Set();
Assert.True(releaseFirst.Wait(TimeSpan.FromSeconds(5)));
return 1;
},
TimeSpan.FromSeconds(10),
CancellationToken.None);
Assert.True(firstStarted.Wait(TimeSpan.FromSeconds(5)));
var second = dispatcher.InvokeAsync(
() =>
{
calls.Enqueue((
2,
Environment.CurrentManagedThreadId,
Thread.CurrentThread.GetApartmentState()));
return 2;
},
TimeSpan.FromSeconds(10),
CancellationToken.None);
var third = dispatcher.InvokeAsync(
() =>
{
calls.Enqueue((
3,
Environment.CurrentManagedThreadId,
Thread.CurrentThread.GetApartmentState()));
return 3;
},
TimeSpan.FromSeconds(10),
CancellationToken.None);
releaseFirst.Set();
var results = await Task.WhenAll(first, second, third);
Assert.Equal(new[] { 1, 2, 3 }, results);
var recorded = calls.ToArray();
Assert.Equal([1, 2, 3], recorded.Select(call => call.Order));
Assert.Single(recorded.Select(call => call.ThreadId).Distinct());
Assert.All(recorded, call => Assert.Equal(ApartmentState.STA, call.Apartment));
Assert.Equal(recorded[0].ThreadId, dispatcher.ManagedThreadId);
}
[Fact]
public async Task InvokeAsync_CancelledWhileQueued_DoesNotRunCallback()
{
await using var dispatcher = new StaDispatcher(capacity: 2);
using var firstStarted = new ManualResetEventSlim();
using var releaseFirst = new ManualResetEventSlim();
var cancelledCallbackRan = false;
var first = dispatcher.InvokeAsync(
() =>
{
firstStarted.Set();
Assert.True(releaseFirst.Wait(TimeSpan.FromSeconds(5)));
return 1;
},
TimeSpan.FromSeconds(10),
CancellationToken.None);
Assert.True(firstStarted.Wait(TimeSpan.FromSeconds(5)));
using var cancellation = new CancellationTokenSource();
var cancelled = dispatcher.InvokeAsync(
() =>
{
cancelledCallbackRan = true;
return 2;
},
TimeSpan.FromSeconds(10),
cancellation.Token);
cancellation.Cancel();
releaseFirst.Set();
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => cancelled);
Assert.Equal(1, await first);
Assert.False(cancelledCallbackRan);
}
[Fact]
public async Task InvokeAsync_WhenBoundedQueueIsFull_CanCancelWithoutRunning()
{
await using var dispatcher = new StaDispatcher(capacity: 1);
using var firstStarted = new ManualResetEventSlim();
using var releaseFirst = new ManualResetEventSlim();
var thirdCallbackRan = false;
var first = dispatcher.InvokeAsync(
() =>
{
firstStarted.Set();
Assert.True(releaseFirst.Wait(TimeSpan.FromSeconds(5)));
return 1;
},
TimeSpan.FromSeconds(10),
CancellationToken.None);
Assert.True(firstStarted.Wait(TimeSpan.FromSeconds(5)));
var second = dispatcher.InvokeAsync(
() => 2,
TimeSpan.FromSeconds(10),
CancellationToken.None);
using var cancellation = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
var third = dispatcher.InvokeAsync(
() =>
{
thirdCallbackRan = true;
return 3;
},
TimeSpan.FromSeconds(10),
cancellation.Token);
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => third);
Assert.False(thirdCallbackRan);
releaseFirst.Set();
var results = await Task.WhenAll(first, second);
Assert.Equal(new[] { 1, 2 }, results);
}
[Fact]
public async Task InvokeAsync_TimeoutQuarantinesDispatcherAndRejectsLaterWork()
{
await using var dispatcher = new StaDispatcher(capacity: 2);
using var callbackStarted = new ManualResetEventSlim();
using var releaseCallback = new ManualResetEventSlim();
var timedOut = dispatcher.InvokeAsync(
() =>
{
callbackStarted.Set();
Assert.True(releaseCallback.Wait(TimeSpan.FromSeconds(5)));
return 1;
},
TimeSpan.FromMilliseconds(100),
CancellationToken.None);
Assert.True(callbackStarted.Wait(TimeSpan.FromSeconds(5)));
await Assert.ThrowsAsync<StaOperationTimedOutException>(() => timedOut);
Assert.True(dispatcher.IsQuarantined);
await Assert.ThrowsAsync<StaDispatcherQuarantinedException>(
() => dispatcher.InvokeAsync(
() => 2,
TimeSpan.FromSeconds(1),
CancellationToken.None));
releaseCallback.Set();
}
}