153 lines
6.1 KiB
C#
153 lines
6.1 KiB
C#
using MBN_STOCK_WEBVIEW.Playout.Runtime;
|
|
|
|
namespace MBN_STOCK_WEBVIEW.Playout.Tests;
|
|
|
|
public sealed class DryRunPlayoutEngineTests
|
|
{
|
|
[Fact]
|
|
public async Task DefaultOptions_AreDryRunAndNeverReportARealConnection()
|
|
{
|
|
await using var engine = CreateEngine(PlayoutMode.DryRun);
|
|
|
|
var result = await engine.ConnectAsync(CancellationToken.None);
|
|
|
|
Assert.Equal(PlayoutMode.DryRun, engine.Status.Mode);
|
|
Assert.Equal(PlayoutConnectionState.DryRunReady, engine.Status.State);
|
|
Assert.True(engine.Status.IsCommandAvailable);
|
|
Assert.False(engine.Status.IsConnected);
|
|
Assert.True(result.IsSuccess);
|
|
Assert.True(result.IsDryRun);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DisabledMode_RejectsEveryOperationWithoutChangingState()
|
|
{
|
|
await using var engine = CreateEngine(PlayoutMode.Disabled);
|
|
var cue = Cue("disabled-scene");
|
|
|
|
var results = new[]
|
|
{
|
|
await engine.ConnectAsync(CancellationToken.None),
|
|
await engine.PrepareAsync(cue, CancellationToken.None),
|
|
await engine.TakeInAsync(CancellationToken.None),
|
|
await engine.NextAsync(cue, CancellationToken.None),
|
|
await engine.TakeOutAsync(
|
|
PlayoutTakeOutScope.All,
|
|
CancellationToken.None),
|
|
await engine.DisconnectAsync(CancellationToken.None)
|
|
};
|
|
|
|
Assert.All(results, result => Assert.Equal(PlayoutResultCode.Rejected, result.Code));
|
|
Assert.All(results, result => Assert.False(result.IsDryRun));
|
|
Assert.Equal(PlayoutConnectionState.Disabled, engine.Status.State);
|
|
Assert.False(engine.Status.IsConnected);
|
|
Assert.False(engine.Status.IsCommandAvailable);
|
|
Assert.Null(engine.Status.PreparedSceneName);
|
|
Assert.Null(engine.Status.OnAirSceneName);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DryRun_TransitionsPreparedAndOnAirSceneStateInCommandOrder()
|
|
{
|
|
await using var engine = CreateEngine(PlayoutMode.DryRun);
|
|
var statuses = new List<PlayoutStatus>();
|
|
engine.StatusChanged += (_, args) => statuses.Add(args.Current);
|
|
|
|
var connect = await engine.ConnectAsync(CancellationToken.None);
|
|
var prepare = await engine.PrepareAsync(
|
|
Cue("first-scene"),
|
|
CancellationToken.None);
|
|
Assert.Equal("first-scene", engine.Status.PreparedSceneName);
|
|
Assert.Null(engine.Status.OnAirSceneName);
|
|
|
|
var takeIn = await engine.TakeInAsync(CancellationToken.None);
|
|
Assert.Null(engine.Status.PreparedSceneName);
|
|
Assert.Equal("first-scene", engine.Status.OnAirSceneName);
|
|
|
|
var next = await engine.NextAsync(
|
|
Cue("next-scene"),
|
|
CancellationToken.None);
|
|
Assert.Null(engine.Status.PreparedSceneName);
|
|
Assert.Equal("next-scene", engine.Status.OnAirSceneName);
|
|
|
|
var takeOut = await engine.TakeOutAsync(
|
|
PlayoutTakeOutScope.Layout,
|
|
CancellationToken.None);
|
|
Assert.Null(engine.Status.OnAirSceneName);
|
|
|
|
var disconnect = await engine.DisconnectAsync(CancellationToken.None);
|
|
|
|
Assert.All(
|
|
new[] { connect, prepare, takeIn, next, takeOut, disconnect },
|
|
result => Assert.Equal(PlayoutResultCode.Success, result.Code));
|
|
Assert.All(
|
|
new[] { connect, prepare, takeIn, next, takeOut, disconnect },
|
|
result => Assert.True(result.IsDryRun));
|
|
Assert.Equal(6, statuses.Count);
|
|
Assert.True(statuses.Zip(statuses.Skip(1), (left, right) => left.Sequence < right.Sequence).All(x => x));
|
|
Assert.All(statuses, status => Assert.Equal(PlayoutConnectionState.DryRunReady, status.State));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DryRun_TakeInWithoutPreparedSceneIsRejected()
|
|
{
|
|
await using var engine = CreateEngine(PlayoutMode.DryRun);
|
|
|
|
var result = await engine.TakeInAsync(CancellationToken.None);
|
|
|
|
Assert.Equal(PlayoutResultCode.Rejected, result.Code);
|
|
Assert.Null(engine.Status.OnAirSceneName);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CancelledDryRunCommand_DoesNotChangeStatus()
|
|
{
|
|
await using var engine = CreateEngine(PlayoutMode.DryRun);
|
|
var initialStatus = engine.Status;
|
|
using var cancellation = new CancellationTokenSource();
|
|
cancellation.Cancel();
|
|
|
|
var result = await engine.PrepareAsync(Cue("cancelled-scene"), cancellation.Token);
|
|
|
|
Assert.Equal(PlayoutResultCode.Cancelled, result.Code);
|
|
Assert.Same(initialStatus, engine.Status);
|
|
Assert.Null(engine.Status.PreparedSceneName);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DryRun_PublicStatusAndResultDoNotExposeScenePath()
|
|
{
|
|
const string secretPath = "C:\\private-vendor-assets\\do-not-log\\secret.t2s";
|
|
await using var engine = CreateEngine(PlayoutMode.DryRun);
|
|
var cue = new PlayoutCue(secretPath, "C:\\unsafe-scene-name");
|
|
|
|
var result = await engine.PrepareAsync(cue, CancellationToken.None);
|
|
|
|
Assert.True(result.IsSuccess);
|
|
Assert.Equal("장면", engine.Status.PreparedSceneName);
|
|
Assert.DoesNotContain(secretPath, result.Message, StringComparison.OrdinalIgnoreCase);
|
|
Assert.DoesNotContain(secretPath, engine.Status.Message, StringComparison.OrdinalIgnoreCase);
|
|
Assert.DoesNotContain(cue.SceneName, result.Message, StringComparison.OrdinalIgnoreCase);
|
|
Assert.DoesNotContain(cue.SceneName, engine.Status.Message, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private static PlayoutCue Cue(string sceneName) => new(
|
|
$"C:\\test-only\\{sceneName}.t2s",
|
|
sceneName,
|
|
[new PlayoutField("headline", "safe value", true)]);
|
|
|
|
private static TornadoPlayoutEngine CreateEngine(PlayoutMode mode)
|
|
{
|
|
var options = ValidatedPlayoutOptions.Create(new PlayoutOptions { Mode = mode });
|
|
return new TornadoPlayoutEngine(
|
|
options,
|
|
new FakeK3dSessionFactory(),
|
|
new FakeRegistrationProbe(),
|
|
new FakeTornadoProcessProbe(new TornadoProcessSnapshot(0, 0, 0)),
|
|
null,
|
|
new FakeLiveAuthorization(false),
|
|
TimeProvider.System,
|
|
startMonitor: false);
|
|
}
|
|
}
|