feat: migrate legacy playout workflow and scenes
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using MBN_STOCK_WEBVIEW.Playout.Configuration;
|
||||
using MBN_STOCK_WEBVIEW.Playout.Diagnostics;
|
||||
using MBN_STOCK_WEBVIEW.Playout.Interop;
|
||||
using MBN_STOCK_WEBVIEW.Playout.Runtime;
|
||||
|
||||
@@ -331,8 +332,8 @@ public sealed class DynamicK3dSessionTests
|
||||
"SetVisible:headline:1",
|
||||
"GetObject:badge",
|
||||
"SetVisible:badge:0",
|
||||
"EndTransactionOnChannel:9",
|
||||
"QueryVariables",
|
||||
"EndTransactionOnChannel:9",
|
||||
"Prepare:10",
|
||||
"Play:10",
|
||||
expectedTakeOutCall,
|
||||
@@ -343,6 +344,495 @@ public sealed class DynamicK3dSessionTests
|
||||
Assert.All(log.ApartmentStates, state => Assert.Equal(ApartmentState.STA, state));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Prepare_AppliesEveryLegacyMutationInOriginalTransactionOrder()
|
||||
{
|
||||
using var scenes = TemporarySceneDirectory.Create(
|
||||
"test-scene.t2s",
|
||||
"Images\\badge.png",
|
||||
"Video\\background.vrv");
|
||||
var options = ValidatedPlayoutOptions.Create(TestOptions(scenes.Path));
|
||||
var cue = options.ResolveCue(new PlayoutCue(
|
||||
"test-scene.t2s",
|
||||
"test-scene",
|
||||
FadeDuration: 4,
|
||||
Mutations:
|
||||
[
|
||||
new PlayoutUseBackground(true),
|
||||
new PlayoutSetBackgroundTexture("Video/background.vrv"),
|
||||
new PlayoutSetBackgroundVideo("Video/background.vrv", 2, true),
|
||||
new PlayoutSetValue("headline", "value"),
|
||||
new PlayoutSetBackgroundVideo(
|
||||
"Video/background.vrv",
|
||||
3,
|
||||
true,
|
||||
PlayoutMutationTiming.InTransaction),
|
||||
new PlayoutSetAssetValue("badge", "Images/badge.png"),
|
||||
new PlayoutSetVisible("badge", false),
|
||||
new PlayoutSetFaceColor("headline", 1, 2, 3, 4),
|
||||
new PlayoutSetPosition("position", 1, 2, 3, PlayoutVectorComponents.XYZ),
|
||||
new PlayoutSetPositionKey("position", 5, 6, 7, 8, PlayoutVectorComponents.Y),
|
||||
new PlayoutSetScale("scale", 9, 10, 11, PlayoutVectorComponents.X),
|
||||
new PlayoutSetCropKey("crop", 1, 12, 13, 14, 15, PlayoutCropEdges.Vertical),
|
||||
new PlayoutSetCircleAngleKey("circle", 2, 16, 17, PlayoutAngleComponents.All),
|
||||
new PlayoutSetPathPoints(
|
||||
"path",
|
||||
[new PlayoutPoint(18, 19, 20), new PlayoutPoint(21, 22, 23)]),
|
||||
new PlayoutSetPathShapePoints(
|
||||
"shape",
|
||||
[new PlayoutPoint(24, 25, 26)])
|
||||
]));
|
||||
var log = new FakeComLog();
|
||||
var scene = new FakeScene(log);
|
||||
var activator = new FakeActivator(
|
||||
log,
|
||||
new FakeEngine(log, new FakePlayer(log), scene));
|
||||
await using var dispatcher = new StaDispatcher(capacity: 2);
|
||||
|
||||
await dispatcher.InvokeAsync(
|
||||
() =>
|
||||
{
|
||||
using var session = new DynamicK3dSession(activator);
|
||||
session.Connect(options);
|
||||
session.Prepare(cue, options.LayoutIndex);
|
||||
session.Disconnect();
|
||||
return true;
|
||||
},
|
||||
TimeSpan.FromSeconds(5),
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"LoadScene:test-scene",
|
||||
"SetOutputChannelIndex:9",
|
||||
"SetSceneEffectType:1:7:4",
|
||||
"UseBackground:1",
|
||||
"SetBackgroundTexture:background.vrv",
|
||||
"SetBackgroundVideo:background.vrv:2:1",
|
||||
"BeginTransaction",
|
||||
"GetObject:headline",
|
||||
"SetValue:headline:value",
|
||||
"SetBackgroundVideo:background.vrv:3:1",
|
||||
"GetObject:badge",
|
||||
"SetValue:badge:badge.png",
|
||||
"GetObject:badge",
|
||||
"SetVisible:badge:0",
|
||||
"GetObject:headline",
|
||||
"SetFaceColor:headline:1:2:3:4",
|
||||
"GetObject:position",
|
||||
"SetPosition:position:1:2:3:7",
|
||||
"GetObject:position",
|
||||
"SetPositionKey:position:5:6:7:8:2",
|
||||
"GetObject:scale",
|
||||
"SetScale:scale:9:10:11:1",
|
||||
"GetObject:crop",
|
||||
"SetCropKey:crop:1:12:13:14:15:10",
|
||||
"GetObject:circle",
|
||||
"SetCircleAngleKey:circle:2:16:17:3",
|
||||
"GetObject:path",
|
||||
"BeginPathPoint:path",
|
||||
"ClearPathPoints:path",
|
||||
"AddPathPoint:path:18:19:20",
|
||||
"AddPathPoint:path:21:22:23",
|
||||
"EndPathPoint:path",
|
||||
"GetObject:shape",
|
||||
"BeginPathShapePoint:shape",
|
||||
"ClearPathShapePoints:shape",
|
||||
"AddPathShapePoint:shape:24:25:26",
|
||||
"EndPathShapePoint:shape",
|
||||
"QueryVariables",
|
||||
"EndTransactionOnChannel:9",
|
||||
"Prepare:10"
|
||||
},
|
||||
log.Names.Where(name =>
|
||||
name.StartsWith("LoadScene:", StringComparison.Ordinal) ||
|
||||
name.StartsWith("Set", StringComparison.Ordinal) ||
|
||||
name.StartsWith("Use", StringComparison.Ordinal) ||
|
||||
name.StartsWith("Begin", StringComparison.Ordinal) ||
|
||||
name.StartsWith("Clear", StringComparison.Ordinal) ||
|
||||
name.StartsWith("Add", StringComparison.Ordinal) ||
|
||||
name.StartsWith("End", StringComparison.Ordinal) ||
|
||||
name.StartsWith("GetObject:", StringComparison.Ordinal) ||
|
||||
name == "QueryVariables" || name == "Prepare:10"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateOnAir_UsesPlayingSceneWithoutReloadingOrReapplyingSceneSetup()
|
||||
{
|
||||
using var scenes = TemporarySceneDirectory.Create(
|
||||
"test-scene.t2s",
|
||||
"Video\\background.vrv");
|
||||
var options = ValidatedPlayoutOptions.Create(TestOptions(scenes.Path));
|
||||
var initialCue = SceneCue(options, "test-scene");
|
||||
var updateCue = options.ResolveCue(new PlayoutCue(
|
||||
"test-scene.t2s",
|
||||
"test-scene",
|
||||
[new PlayoutField("headline", "page two", true)],
|
||||
FadeDuration: 59,
|
||||
Mutations:
|
||||
[
|
||||
new PlayoutUseBackground(false),
|
||||
new PlayoutSetBackgroundTexture("Video/background.vrv"),
|
||||
new PlayoutSetBackgroundVideo("Video/background.vrv", 4, true),
|
||||
new PlayoutSetVisible("badge", false)
|
||||
]));
|
||||
var log = new FakeComLog();
|
||||
var scene = new FakeScene(log, "test-scene");
|
||||
var player = new FakePlayer(log) { PlayingScene = scene };
|
||||
var activator = new FakeActivator(log, new FakeEngine(log, player, scene));
|
||||
await using var dispatcher = new StaDispatcher(capacity: 2);
|
||||
var updateStart = 0;
|
||||
var updateEnd = 0;
|
||||
|
||||
await dispatcher.InvokeAsync(
|
||||
() =>
|
||||
{
|
||||
using var session = new DynamicK3dSession(activator);
|
||||
session.Connect(options);
|
||||
session.Prepare(initialCue, options.LayoutIndex);
|
||||
session.Play(options.LayoutIndex);
|
||||
|
||||
updateStart = log.Names.Length;
|
||||
session.UpdateOnAir(updateCue, options.LayoutIndex);
|
||||
updateEnd = log.Names.Length;
|
||||
session.Disconnect();
|
||||
return true;
|
||||
},
|
||||
TimeSpan.FromSeconds(5),
|
||||
CancellationToken.None);
|
||||
|
||||
var updateCalls = log.Names
|
||||
.Skip(updateStart)
|
||||
.Take(updateEnd - updateStart)
|
||||
.ToArray();
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"Play:10",
|
||||
"GetPlayingScene:10",
|
||||
"BeginTransaction",
|
||||
"GetObject:headline",
|
||||
"SetValue:headline:page two",
|
||||
"SetVisible:headline:1",
|
||||
"GetObject:badge",
|
||||
"SetVisible:badge:0",
|
||||
"QueryVariables",
|
||||
"EndTransactionOnChannel:9",
|
||||
"Prepare:10",
|
||||
"Play:10"
|
||||
},
|
||||
updateCalls);
|
||||
Assert.DoesNotContain(updateCalls, call =>
|
||||
call.StartsWith("LoadScene:", StringComparison.Ordinal) ||
|
||||
call.StartsWith("SetSceneEffectType:", StringComparison.Ordinal) ||
|
||||
call.StartsWith("UseBackground:", StringComparison.Ordinal) ||
|
||||
call.StartsWith("SetBackgroundTexture:", StringComparison.Ordinal) ||
|
||||
call.StartsWith("SetBackgroundVideo:", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateOnAir_CallbackSession_TracksItsPlayUntilMatchingCallback()
|
||||
{
|
||||
using var scenes = TemporarySceneDirectory.Create("5001.t2s");
|
||||
var options = SceneOptions(scenes.Path, "5001");
|
||||
var cue = SceneCue(options, "5001");
|
||||
var log = new FakeComLog();
|
||||
var scene = new FakeScene(log, "5001");
|
||||
var player = new FakePlayer(log) { PlayingScene = scene };
|
||||
var engine = new FakeEngine(log, player, scene);
|
||||
var activator = new FakeActivator(log, engine);
|
||||
var callbacks = new RecordingEventHandlerFactory();
|
||||
var releaser = new FakeReleaser(
|
||||
log,
|
||||
(scene, "5001"),
|
||||
(player, "Player"),
|
||||
(engine, "Engine"),
|
||||
(callbacks.Handler, "EventHandler"));
|
||||
await using var dispatcher = new StaDispatcher(capacity: 2);
|
||||
|
||||
await dispatcher.InvokeAsync(
|
||||
() =>
|
||||
{
|
||||
using var session = new DynamicK3dSession(
|
||||
activator,
|
||||
releaser,
|
||||
new InstalledK3dInteropMethodInvoker(),
|
||||
callbacks);
|
||||
session.Connect(options);
|
||||
session.Prepare(cue, options.LayoutIndex);
|
||||
session.Play(options.LayoutIndex);
|
||||
callbacks.Fire("OnScenePlayed", 1, 8, 10);
|
||||
session.ProcessPendingCallbacks(options.LayoutIndex);
|
||||
Assert.True(session.HasPendingLifecycleCallbacks);
|
||||
|
||||
callbacks.Fire("OnScenePlayed", 1, 9, 10);
|
||||
session.ProcessPendingCallbacks(options.LayoutIndex);
|
||||
Assert.False(session.HasPendingLifecycleCallbacks);
|
||||
|
||||
session.UpdateOnAir(cue, options.LayoutIndex);
|
||||
Assert.True(session.HasPendingLifecycleCallbacks);
|
||||
|
||||
callbacks.Fire("OnScenePlayed", 1, 8, 10);
|
||||
session.ProcessPendingCallbacks(options.LayoutIndex);
|
||||
Assert.True(session.HasPendingLifecycleCallbacks);
|
||||
|
||||
callbacks.Fire("OnScenePlayed", 1, 9, 10);
|
||||
session.ProcessPendingCallbacks(options.LayoutIndex);
|
||||
Assert.True(session.HasPendingLifecycleCallbacks);
|
||||
|
||||
callbacks.Fire("OnScenePlayed", 1, 9, 10);
|
||||
session.ProcessPendingCallbacks(options.LayoutIndex);
|
||||
Assert.False(session.HasPendingLifecycleCallbacks);
|
||||
session.Disconnect();
|
||||
return true;
|
||||
},
|
||||
TimeSpan.FromSeconds(5),
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.Equal(3, log.Names.Count(name => name == "Play:10"));
|
||||
Assert.Equal(1, log.Names.Count(name => name == "GetPlayingScene:10"));
|
||||
Assert.DoesNotContain("Unload:5001", log.Names);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LifecycleCallbacks_UnloadOnlyRetiredScenesAfterMatchingSuccess()
|
||||
{
|
||||
using var scenes = TemporarySceneDirectory.Create("5001.t2s", "5006.t2s");
|
||||
var options = SceneOptions(scenes.Path, "5001", "5006");
|
||||
var log = new FakeComLog();
|
||||
var player = new FakePlayer(log);
|
||||
var scene5001 = new FakeScene(log, "5001");
|
||||
var scene5006 = new FakeScene(log, "5006");
|
||||
var engine = new FakeEngine(log, player, scene5001)
|
||||
{
|
||||
SceneResolver = name => name == "5006" ? scene5006 : scene5001
|
||||
};
|
||||
var activator = new FakeActivator(log, engine);
|
||||
var callbacks = new RecordingEventHandlerFactory();
|
||||
var releaser = new FakeReleaser(
|
||||
log,
|
||||
(scene5001, "5001"),
|
||||
(scene5006, "5006"),
|
||||
(player, "Player"),
|
||||
(engine, "Engine"),
|
||||
(callbacks.Handler, "EventHandler"));
|
||||
await using var dispatcher = new StaDispatcher(capacity: 3);
|
||||
|
||||
await dispatcher.InvokeAsync(
|
||||
() =>
|
||||
{
|
||||
using var session = new DynamicK3dSession(
|
||||
activator,
|
||||
releaser,
|
||||
new InstalledK3dInteropMethodInvoker(),
|
||||
callbacks);
|
||||
session.Connect(options);
|
||||
Assert.True(session.SupportsLifecycleCallbacks);
|
||||
Assert.False(session.KtapHelloObserved);
|
||||
|
||||
callbacks.Fire("OnHello");
|
||||
session.Prepare(SceneCue(options, "5001"), options.LayoutIndex);
|
||||
session.Play(options.LayoutIndex);
|
||||
callbacks.Fire("OnScenePlayed", 1, 9, 10);
|
||||
var first = session.ProcessPendingCallbacks(options.LayoutIndex);
|
||||
Assert.True(first.HelloObserved);
|
||||
Assert.Equal(0, first.UnloadedSceneCount);
|
||||
|
||||
session.Prepare(SceneCue(options, "5006"), options.LayoutIndex);
|
||||
session.Play(options.LayoutIndex);
|
||||
Assert.DoesNotContain("Unload:5001", log.Names);
|
||||
|
||||
callbacks.Fire("OnScenePlayed", 1, 8, 10);
|
||||
Assert.Equal(
|
||||
0,
|
||||
session.ProcessPendingCallbacks(options.LayoutIndex).UnloadedSceneCount);
|
||||
Assert.DoesNotContain("Unload:5001", log.Names);
|
||||
|
||||
callbacks.Fire("OnScenePlayed", 1, 9, 10);
|
||||
Assert.Equal(
|
||||
1,
|
||||
session.ProcessPendingCallbacks(options.LayoutIndex).UnloadedSceneCount);
|
||||
Assert.Contains("Unload:5001", log.Names);
|
||||
Assert.Contains("Release:5001", log.Names);
|
||||
|
||||
session.TakeOut(options.LayoutIndex, PlayoutTakeOutScope.All);
|
||||
Assert.True(session.HasPendingLifecycleCallbacks);
|
||||
callbacks.Fire("OnScenePlayed", 1, 9, 10);
|
||||
Assert.Equal(
|
||||
0,
|
||||
session.ProcessPendingCallbacks(options.LayoutIndex).UnloadedSceneCount);
|
||||
Assert.DoesNotContain("Unload:5006", log.Names);
|
||||
|
||||
callbacks.Fire("OnStopAll", 1);
|
||||
Assert.Equal(
|
||||
1,
|
||||
session.ProcessPendingCallbacks(options.LayoutIndex).UnloadedSceneCount);
|
||||
Assert.Contains("Unload:5006", log.Names);
|
||||
Assert.Contains("Release:5006", log.Names);
|
||||
Assert.False(session.HasPendingLifecycleCallbacks);
|
||||
session.Disconnect();
|
||||
return true;
|
||||
},
|
||||
TimeSpan.FromSeconds(5),
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.Equal(1, log.Names.Count(name => name == "Unload:5001"));
|
||||
Assert.Equal(1, log.Names.Count(name => name == "Unload:5006"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StopAllSuccess_ClearsInterruptedPlayBeforeDisconnect()
|
||||
{
|
||||
using var scenes = TemporarySceneDirectory.Create("5001.t2s");
|
||||
var options = SceneOptions(scenes.Path, "5001");
|
||||
var log = new FakeComLog();
|
||||
var scene = new FakeScene(log, "5001");
|
||||
var player = new FakePlayer(log);
|
||||
var engine = new FakeEngine(log, player, scene);
|
||||
var activator = new FakeActivator(log, engine);
|
||||
var callbacks = new RecordingEventHandlerFactory();
|
||||
var releaser = new FakeReleaser(
|
||||
log,
|
||||
(scene, "5001"),
|
||||
(player, "Player"),
|
||||
(engine, "Engine"),
|
||||
(callbacks.Handler, "EventHandler"));
|
||||
await using var dispatcher = new StaDispatcher(capacity: 2);
|
||||
|
||||
await dispatcher.InvokeAsync(
|
||||
() =>
|
||||
{
|
||||
using var session = new DynamicK3dSession(
|
||||
activator,
|
||||
releaser,
|
||||
new InstalledK3dInteropMethodInvoker(),
|
||||
callbacks);
|
||||
session.Connect(options);
|
||||
session.Prepare(SceneCue(options, "5001"), options.LayoutIndex);
|
||||
session.Play(options.LayoutIndex);
|
||||
Assert.True(session.HasPendingPlayCallbacks);
|
||||
|
||||
session.TakeOut(options.LayoutIndex, PlayoutTakeOutScope.All);
|
||||
callbacks.Fire("OnStopAll", 1);
|
||||
var drained = session.ProcessPendingCallbacks(options.LayoutIndex);
|
||||
|
||||
Assert.Equal(1, drained.UnloadedSceneCount);
|
||||
Assert.False(session.HasPendingPlayCallbacks);
|
||||
Assert.False(session.HasPendingLifecycleCallbacks);
|
||||
session.Disconnect();
|
||||
return true;
|
||||
},
|
||||
TimeSpan.FromSeconds(5),
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.Contains("StopAll", log.Names);
|
||||
Assert.Contains("Unload:5001", log.Names);
|
||||
Assert.Contains("Disconnect", log.Names);
|
||||
Assert.Equal(1, log.Names.Count(name => name == "Unload:5001"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CutOutSuccess_ClearsOnlyAfterMatchingOutputBeforeDisconnect()
|
||||
{
|
||||
using var scenes = TemporarySceneDirectory.Create("5001.t2s");
|
||||
var options = SceneOptions(scenes.Path, "5001");
|
||||
var log = new FakeComLog();
|
||||
var scene = new FakeScene(log, "5001");
|
||||
var player = new FakePlayer(log);
|
||||
var engine = new FakeEngine(log, player, scene);
|
||||
var activator = new FakeActivator(log, engine);
|
||||
var callbacks = new RecordingEventHandlerFactory();
|
||||
var releaser = new FakeReleaser(
|
||||
log,
|
||||
(scene, "5001"),
|
||||
(player, "Player"),
|
||||
(engine, "Engine"),
|
||||
(callbacks.Handler, "EventHandler"));
|
||||
await using var dispatcher = new StaDispatcher(capacity: 2);
|
||||
|
||||
await dispatcher.InvokeAsync(
|
||||
() =>
|
||||
{
|
||||
using var session = new DynamicK3dSession(
|
||||
activator,
|
||||
releaser,
|
||||
new InstalledK3dInteropMethodInvoker(),
|
||||
callbacks);
|
||||
session.Connect(options);
|
||||
session.Prepare(SceneCue(options, "5001"), options.LayoutIndex);
|
||||
session.Play(options.LayoutIndex);
|
||||
session.TakeOut(options.LayoutIndex, PlayoutTakeOutScope.Layout);
|
||||
|
||||
callbacks.Fire("OnCutOut", 1, 8, options.LayoutIndex);
|
||||
Assert.Equal(
|
||||
0,
|
||||
session.ProcessPendingCallbacks(options.LayoutIndex).UnloadedSceneCount);
|
||||
Assert.True(session.HasPendingPlayCallbacks);
|
||||
Assert.True(session.HasPendingLifecycleCallbacks);
|
||||
Assert.DoesNotContain("Unload:5001", log.Names);
|
||||
|
||||
callbacks.Fire("OnCutOut", 1, 9, options.LayoutIndex);
|
||||
Assert.Equal(
|
||||
1,
|
||||
session.ProcessPendingCallbacks(options.LayoutIndex).UnloadedSceneCount);
|
||||
Assert.False(session.HasPendingPlayCallbacks);
|
||||
Assert.False(session.HasPendingLifecycleCallbacks);
|
||||
session.Disconnect();
|
||||
return true;
|
||||
},
|
||||
TimeSpan.FromSeconds(5),
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.Contains("CutOut:10", log.Names);
|
||||
Assert.Contains("Unload:5001", log.Names);
|
||||
Assert.Contains("Disconnect", log.Names);
|
||||
Assert.Equal(1, log.Names.Count(name => name == "Unload:5001"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LifecycleCallbackFailure_DoesNotUnloadAndFailsClosed()
|
||||
{
|
||||
using var scenes = TemporarySceneDirectory.Create("5001.t2s");
|
||||
var options = SceneOptions(scenes.Path, "5001");
|
||||
var log = new FakeComLog();
|
||||
var player = new FakePlayer(log);
|
||||
var scene = new FakeScene(log, "5001");
|
||||
var engine = new FakeEngine(log, player, scene);
|
||||
var activator = new FakeActivator(log, engine);
|
||||
var callbacks = new RecordingEventHandlerFactory();
|
||||
var releaser = new FakeReleaser(
|
||||
log,
|
||||
(scene, "5001"),
|
||||
(player, "Player"),
|
||||
(engine, "Engine"),
|
||||
(callbacks.Handler, "EventHandler"));
|
||||
await using var dispatcher = new StaDispatcher(capacity: 2);
|
||||
|
||||
await dispatcher.InvokeAsync(
|
||||
() =>
|
||||
{
|
||||
using var session = new DynamicK3dSession(
|
||||
activator,
|
||||
releaser,
|
||||
new InstalledK3dInteropMethodInvoker(),
|
||||
callbacks);
|
||||
session.Connect(options);
|
||||
session.Prepare(SceneCue(options, "5001"), options.LayoutIndex);
|
||||
session.Play(options.LayoutIndex);
|
||||
session.TakeOut(options.LayoutIndex, PlayoutTakeOutScope.All);
|
||||
callbacks.Fire("OnStopAll", 0);
|
||||
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => session.ProcessPendingCallbacks(options.LayoutIndex));
|
||||
Assert.DoesNotContain("Unload:5001", log.Names);
|
||||
session.Abandon();
|
||||
return true;
|
||||
},
|
||||
TimeSpan.FromSeconds(5),
|
||||
CancellationToken.None);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Prepare_WithoutOutputChannel_UsesDefaultPlayerAndDoesNotSetSceneChannel()
|
||||
{
|
||||
@@ -385,8 +875,8 @@ public sealed class DynamicK3dSessionTests
|
||||
"LoadScene:test-scene",
|
||||
"SetSceneEffectType:1:7:12",
|
||||
"BeginTransaction",
|
||||
"EndTransaction",
|
||||
"QueryVariables",
|
||||
"EndTransaction",
|
||||
"Prepare:10",
|
||||
"Disconnect"
|
||||
},
|
||||
@@ -566,6 +1056,7 @@ public sealed class DynamicK3dSessionTests
|
||||
"Prepare:10",
|
||||
"Play:10",
|
||||
"LoadScene:5006",
|
||||
"RollbackTransaction",
|
||||
"StopAll",
|
||||
"Disconnect",
|
||||
"Release:5006",
|
||||
@@ -853,6 +1344,25 @@ public sealed class DynamicK3dSessionTests
|
||||
public object Create(Guid classId) => throw _exception;
|
||||
}
|
||||
|
||||
private sealed class RecordingEventHandlerFactory : IK3dEventHandlerFactory
|
||||
{
|
||||
private IK3dEventSink? _sink;
|
||||
|
||||
public bool SupportsCallbacks => true;
|
||||
|
||||
public object Handler { get; } = new();
|
||||
|
||||
public object Create(IK3dEventSink sink)
|
||||
{
|
||||
_sink = sink;
|
||||
return Handler;
|
||||
}
|
||||
|
||||
public void Fire(string methodName, params object?[] arguments) =>
|
||||
(_sink ?? throw new InvalidOperationException("Callback sink is not connected."))
|
||||
.OnCallback(methodName, arguments);
|
||||
}
|
||||
|
||||
private sealed class BlockingEngineActivator : ILateBoundComActivator
|
||||
{
|
||||
private readonly FakeComLog _log;
|
||||
@@ -1022,6 +1532,15 @@ public sealed class DynamicK3dSessionTests
|
||||
_playFailure = playFailure;
|
||||
}
|
||||
|
||||
public object? PlayingScene { get; set; }
|
||||
|
||||
public object GetPlayingScene(int layoutIndex)
|
||||
{
|
||||
_log.Add($"GetPlayingScene:{layoutIndex}");
|
||||
return PlayingScene
|
||||
?? throw new InvalidOperationException("No fake playing scene is configured.");
|
||||
}
|
||||
|
||||
public void Prepare(int layoutIndex, object scene)
|
||||
{
|
||||
Assert.NotNull(scene);
|
||||
@@ -1067,6 +1586,14 @@ public sealed class DynamicK3dSessionTests
|
||||
public void SetSceneEffectType(int inEffect, int effectType, int duration) =>
|
||||
_log.Add($"SetSceneEffectType:{inEffect}:{effectType}:{duration}");
|
||||
|
||||
public void UseBackground(int enabled) => _log.Add($"UseBackground:{enabled}");
|
||||
|
||||
public void SetBackgroundTexture(string path) =>
|
||||
_log.Add($"SetBackgroundTexture:{System.IO.Path.GetFileName(path)}");
|
||||
|
||||
public void SetBackgroundVideo(string path, int loopCount, int loopInfinite) =>
|
||||
_log.Add($"SetBackgroundVideo:{System.IO.Path.GetFileName(path)}:{loopCount}:{loopInfinite}");
|
||||
|
||||
public object GetObject(string objectName)
|
||||
{
|
||||
_log.Add($"GetObject:{objectName}");
|
||||
@@ -1103,8 +1630,58 @@ public sealed class DynamicK3dSessionTests
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public void SetValue(string value) => _log.Add($"SetValue:{_name}:{value}");
|
||||
public void SetValue(string value) => _log.Add(
|
||||
$"SetValue:{_name}:{(System.IO.Path.IsPathFullyQualified(value) ? System.IO.Path.GetFileName(value) : value)}");
|
||||
|
||||
public void SetVisible(int visible) => _log.Add($"SetVisible:{_name}:{visible}");
|
||||
|
||||
public void SetFaceColor(int red, int green, int blue, int alpha) =>
|
||||
_log.Add($"SetFaceColor:{_name}:{red}:{green}:{blue}:{alpha}");
|
||||
|
||||
public void SetPosition(float x, float y, float z, int components) =>
|
||||
_log.Add(FormattableString.Invariant($"SetPosition:{_name}:{x}:{y}:{z}:{components}"));
|
||||
|
||||
public void SetPositionKey(int keyIndex, float x, float y, float z, int components) =>
|
||||
_log.Add(FormattableString.Invariant(
|
||||
$"SetPositionKey:{_name}:{keyIndex}:{x}:{y}:{z}:{components}"));
|
||||
|
||||
public void SetScale(float x, float y, float z, int components) =>
|
||||
_log.Add(FormattableString.Invariant($"SetScale:{_name}:{x}:{y}:{z}:{components}"));
|
||||
|
||||
public void SetCropKey(
|
||||
int keyIndex,
|
||||
float left,
|
||||
float top,
|
||||
float right,
|
||||
float bottom,
|
||||
int edges) =>
|
||||
_log.Add(FormattableString.Invariant(
|
||||
$"SetCropKey:{_name}:{keyIndex}:{left}:{top}:{right}:{bottom}:{edges}"));
|
||||
|
||||
public void SetCircleAngleKey(
|
||||
int keyIndex,
|
||||
float start,
|
||||
float end,
|
||||
int components) =>
|
||||
_log.Add(FormattableString.Invariant(
|
||||
$"SetCircleAngleKey:{_name}:{keyIndex}:{start}:{end}:{components}"));
|
||||
|
||||
public void BeginPathPoint() => _log.Add($"BeginPathPoint:{_name}");
|
||||
|
||||
public void ClearPathPoints() => _log.Add($"ClearPathPoints:{_name}");
|
||||
|
||||
public void AddPathPoint(float x, float y, float z) =>
|
||||
_log.Add(FormattableString.Invariant($"AddPathPoint:{_name}:{x}:{y}:{z}"));
|
||||
|
||||
public void EndPathPoint() => _log.Add($"EndPathPoint:{_name}");
|
||||
|
||||
public void BeginPathShapePoint() => _log.Add($"BeginPathShapePoint:{_name}");
|
||||
|
||||
public void ClearPathShapePoints() => _log.Add($"ClearPathShapePoints:{_name}");
|
||||
|
||||
public void AddPathShapePoint(float x, float y, float z) =>
|
||||
_log.Add(FormattableString.Invariant($"AddPathShapePoint:{_name}:{x}:{y}:{z}"));
|
||||
|
||||
public void EndPathShapePoint() => _log.Add($"EndPathShapePoint:{_name}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user