249 lines
6.7 KiB
C#
249 lines
6.7 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Text.RegularExpressions;
|
|
using MBN_STOCK_WEBVIEW.Playout.Configuration;
|
|
using MBN_STOCK_WEBVIEW.Playout.Interop;
|
|
using MBN_STOCK_WEBVIEW.Playout.Registration;
|
|
using MBN_STOCK_WEBVIEW.Playout.Runtime;
|
|
using MBN_STOCK_WEBVIEW.Playout.Safety;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.Playout.Tests;
|
|
|
|
internal sealed class FakeK3dSessionFactory : IK3dSessionFactory
|
|
{
|
|
private readonly Func<FakeK3dSession> _create;
|
|
private int _createCount;
|
|
|
|
public FakeK3dSessionFactory(Func<FakeK3dSession>? create = null)
|
|
{
|
|
_create = create ?? (() => new FakeK3dSession());
|
|
}
|
|
|
|
public int CreateCount => Volatile.Read(ref _createCount);
|
|
|
|
public ConcurrentQueue<FakeK3dSession> Sessions { get; } = new();
|
|
|
|
public IK3dSession Create()
|
|
{
|
|
Interlocked.Increment(ref _createCount);
|
|
var session = _create();
|
|
Sessions.Enqueue(session);
|
|
return session;
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeK3dSession : IK3dSession
|
|
{
|
|
private int _disposed;
|
|
private int _ktapDispatchGate;
|
|
private int _ktapDispatchCount;
|
|
|
|
public bool IsConnected { get; private set; }
|
|
|
|
public PlayoutKtapConnectState LastKtapConnectState { get; private set; }
|
|
|
|
public ConcurrentQueue<string> Calls { get; } = new();
|
|
|
|
public ConcurrentQueue<int> ThreadIds { get; } = new();
|
|
|
|
public ConcurrentQueue<ApartmentState> ApartmentStates { get; } = new();
|
|
|
|
public Action<ValidatedPlayoutOptions>? ConnectAction { get; set; }
|
|
|
|
public Action? BeforeKtapDispatchAction { get; set; }
|
|
|
|
public int KtapDispatchCount => Volatile.Read(ref _ktapDispatchCount);
|
|
|
|
public Action? DisconnectAction { get; set; }
|
|
|
|
public Action<PlayoutCue, int>? PrepareAction { get; set; }
|
|
|
|
public Action<int>? PlayAction { get; set; }
|
|
|
|
public Action<int, PlayoutTakeOutScope>? TakeOutAction { get; set; }
|
|
|
|
public Action? AbandonAction { get; set; }
|
|
|
|
public Action<string>? CallObserver { get; set; }
|
|
|
|
public void Connect(ValidatedPlayoutOptions options)
|
|
{
|
|
Record("Connect");
|
|
BeforeKtapDispatchAction?.Invoke();
|
|
if (Interlocked.CompareExchange(ref _ktapDispatchGate, 1, 0) != 0)
|
|
{
|
|
throw new InvalidOperationException("Fake KTAP dispatch was prevented.");
|
|
}
|
|
|
|
LastKtapConnectState = PlayoutKtapConnectState.Attempted;
|
|
Interlocked.Increment(ref _ktapDispatchCount);
|
|
try
|
|
{
|
|
ConnectAction?.Invoke(options);
|
|
}
|
|
catch
|
|
{
|
|
LastKtapConnectState = PlayoutKtapConnectState.Failed;
|
|
throw;
|
|
}
|
|
|
|
LastKtapConnectState = PlayoutKtapConnectState.AcceptedUnconfirmed;
|
|
IsConnected = true;
|
|
}
|
|
|
|
public bool TryPreventKtapConnect()
|
|
{
|
|
var previous = Interlocked.CompareExchange(ref _ktapDispatchGate, 2, 0);
|
|
if (previous == 1 && LastKtapConnectState == PlayoutKtapConnectState.NotAttempted)
|
|
{
|
|
LastKtapConnectState = PlayoutKtapConnectState.Attempted;
|
|
}
|
|
|
|
return previous == 0;
|
|
}
|
|
|
|
public void Disconnect()
|
|
{
|
|
Record("Disconnect");
|
|
DisconnectAction?.Invoke();
|
|
IsConnected = false;
|
|
}
|
|
|
|
public void Prepare(PlayoutCue cue, int layoutIndex)
|
|
{
|
|
Record($"Prepare:{cue.SceneName}");
|
|
PrepareAction?.Invoke(cue, layoutIndex);
|
|
}
|
|
|
|
public void Play(int layoutIndex)
|
|
{
|
|
Record("Play");
|
|
PlayAction?.Invoke(layoutIndex);
|
|
}
|
|
|
|
public void TakeOut(int layoutIndex, PlayoutTakeOutScope scope)
|
|
{
|
|
Record($"TakeOut:{scope}");
|
|
TakeOutAction?.Invoke(layoutIndex, scope);
|
|
}
|
|
|
|
public void Abandon()
|
|
{
|
|
TryPreventKtapConnect();
|
|
if (Interlocked.Exchange(ref _disposed, 1) == 0)
|
|
{
|
|
Record("Abandon");
|
|
AbandonAction?.Invoke();
|
|
IsConnected = false;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (Interlocked.Exchange(ref _disposed, 1) == 0)
|
|
{
|
|
Record("Dispose");
|
|
IsConnected = false;
|
|
}
|
|
}
|
|
|
|
private void Record(string name)
|
|
{
|
|
Calls.Enqueue(name);
|
|
ThreadIds.Enqueue(Environment.CurrentManagedThreadId);
|
|
ApartmentStates.Enqueue(Thread.CurrentThread.GetApartmentState());
|
|
CallObserver?.Invoke(name);
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeRegistrationProbe : IK3dRegistrationProbe
|
|
{
|
|
private int _probeCount;
|
|
|
|
public FakeRegistrationProbe(K3dRegistrationReport? report = null)
|
|
{
|
|
Report = report ?? ReadyReport;
|
|
}
|
|
|
|
public static K3dRegistrationReport ReadyReport { get; } = new(
|
|
K3dRegistrationIssue.None,
|
|
Is64BitProcess: true,
|
|
HasWin64TypeLibrary: true,
|
|
IsWin64BinaryAmd64: true,
|
|
IsEngineClassReady: true,
|
|
IsEventHandlerClassReady: true,
|
|
Message: "fake K3D registration ready");
|
|
|
|
public K3dRegistrationReport Report { get; set; }
|
|
|
|
public int ProbeCount => Volatile.Read(ref _probeCount);
|
|
|
|
public K3dRegistrationReport Probe()
|
|
{
|
|
Interlocked.Increment(ref _probeCount);
|
|
return Report;
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeTornadoProcessProbe : ITornadoProcessProbe
|
|
{
|
|
private readonly object _sync = new();
|
|
private readonly Queue<TornadoProcessSnapshot> _snapshots = new();
|
|
private TornadoProcessSnapshot _last;
|
|
|
|
public FakeTornadoProcessProbe(params TornadoProcessSnapshot[] snapshots)
|
|
{
|
|
_last = snapshots.LastOrDefault()
|
|
?? new TornadoProcessSnapshot(0, 0, 0);
|
|
foreach (var snapshot in snapshots)
|
|
{
|
|
_snapshots.Enqueue(snapshot);
|
|
}
|
|
}
|
|
|
|
public int CaptureCount { get; private set; }
|
|
|
|
public Regex? LastPattern { get; private set; }
|
|
|
|
public TornadoProcessSnapshot Capture(Regex? testWindowPattern)
|
|
{
|
|
lock (_sync)
|
|
{
|
|
CaptureCount++;
|
|
LastPattern = testWindowPattern;
|
|
if (_snapshots.Count > 0)
|
|
{
|
|
_last = _snapshots.Dequeue();
|
|
}
|
|
|
|
return _last;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed class FakeLiveAuthorization : ILiveAuthorization
|
|
{
|
|
public FakeLiveAuthorization(bool isAuthorizedForThisLaunch)
|
|
{
|
|
IsAuthorizedForThisLaunch = isAuthorizedForThisLaunch;
|
|
}
|
|
|
|
public bool IsAuthorizedForThisLaunch { get; }
|
|
}
|
|
|
|
internal sealed class TrackingStaDispatcherFactory : IStaDispatcherFactory
|
|
{
|
|
private int _createCount;
|
|
|
|
public int CreateCount => Volatile.Read(ref _createCount);
|
|
|
|
public ConcurrentQueue<IStaDispatcher> Dispatchers { get; } = new();
|
|
|
|
public IStaDispatcher Create(int capacity)
|
|
{
|
|
Interlocked.Increment(ref _createCount);
|
|
var dispatcher = new StaDispatcher(capacity);
|
|
Dispatchers.Enqueue(dispatcher);
|
|
return dispatcher;
|
|
}
|
|
}
|