fix: refresh the retained on-air scene
This commit is contained in:
@@ -550,18 +550,33 @@ internal sealed class DynamicK3dSession : IK3dGuardedConnectSession
|
|||||||
var engine = _engine!;
|
var engine = _engine!;
|
||||||
var player = _player!;
|
var player = _player!;
|
||||||
var outputChannel = _outputChannel;
|
var outputChannel = _outputChannel;
|
||||||
object? playingScene = null;
|
// Tornado2 can return an incomplete GetPlayingScene proxy. The scene loaded,
|
||||||
|
// prepared, and promoted by Play is the only trusted reference for an in-place
|
||||||
|
// refresh, and it remains retained until the normal take-out/unload lifecycle.
|
||||||
|
if (_preparedScene is not null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"An on-air update cannot run while another scene is prepared.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentScene = ReusableScene(_currentScene, cue.SceneName)
|
||||||
|
?? throw new InvalidOperationException(
|
||||||
|
"No matching retained on-air scene is available.");
|
||||||
|
if (SupportsLifecycleCallbacks && _pendingPlayCallbacks != 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"An on-air update cannot run while scene playback is pending.");
|
||||||
|
}
|
||||||
|
|
||||||
var transactionStarted = false;
|
var transactionStarted = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
playingScene = InvokeRequired(player, "GetPlayingScene", layoutIndex);
|
|
||||||
|
|
||||||
foreach (var mutation in cue.Mutations ?? [])
|
foreach (var mutation in cue.Mutations ?? [])
|
||||||
{
|
{
|
||||||
if (IsBeforeTransactionSceneSetupMutation(mutation))
|
if (IsBeforeTransactionSceneSetupMutation(mutation))
|
||||||
{
|
{
|
||||||
ApplySceneSetupMutation(playingScene, mutation);
|
ApplySceneSetupMutation(currentScene.Value, mutation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,7 +585,7 @@ internal sealed class DynamicK3dSession : IK3dGuardedConnectSession
|
|||||||
|
|
||||||
foreach (var field in cue.Fields ?? [])
|
foreach (var field in cue.Fields ?? [])
|
||||||
{
|
{
|
||||||
ApplyField(playingScene, field);
|
ApplyField(currentScene.Value, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var mutation in cue.Mutations ?? [])
|
foreach (var mutation in cue.Mutations ?? [])
|
||||||
@@ -580,11 +595,11 @@ internal sealed class DynamicK3dSession : IK3dGuardedConnectSession
|
|||||||
// explicit in-transaction ordering.
|
// explicit in-transaction ordering.
|
||||||
if (!IsBeforeTransactionSceneSetupMutation(mutation))
|
if (!IsBeforeTransactionSceneSetupMutation(mutation))
|
||||||
{
|
{
|
||||||
ApplyTransactionMutation(playingScene, mutation);
|
ApplyTransactionMutation(currentScene.Value, mutation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Invoke(playingScene, "QueryVariables");
|
Invoke(currentScene.Value, "QueryVariables");
|
||||||
if (outputChannel.HasValue)
|
if (outputChannel.HasValue)
|
||||||
{
|
{
|
||||||
Invoke(engine, "EndTransactionOnChannel", outputChannel.GetValueOrDefault());
|
Invoke(engine, "EndTransactionOnChannel", outputChannel.GetValueOrDefault());
|
||||||
@@ -595,20 +610,8 @@ internal sealed class DynamicK3dSession : IK3dGuardedConnectSession
|
|||||||
}
|
}
|
||||||
|
|
||||||
transactionStarted = false;
|
transactionStarted = false;
|
||||||
Invoke(player, "Prepare", layoutIndex, playingScene);
|
Invoke(player, "Prepare", layoutIndex, currentScene.Value);
|
||||||
InvokeTrackedPlay(layoutIndex);
|
InvokeTrackedPlay(layoutIndex);
|
||||||
|
|
||||||
// GetPlayingScene can return the same RCW or another RCW for the same COM
|
|
||||||
// scene. An in-place page update must never retire/unload the active scene.
|
|
||||||
if (_currentScene is null)
|
|
||||||
{
|
|
||||||
_currentScene = new SceneReference(playingScene, cue.SceneName);
|
|
||||||
playingScene = null;
|
|
||||||
}
|
|
||||||
else if (ReferenceEquals(_currentScene.Value, playingScene))
|
|
||||||
{
|
|
||||||
playingScene = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -626,10 +629,6 @@ internal sealed class DynamicK3dSession : IK3dGuardedConnectSession
|
|||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
ReleaseComObject(playingScene);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TakeOut(int layoutIndex, PlayoutTakeOutScope scope)
|
public void TakeOut(int layoutIndex, PlayoutTakeOutScope scope)
|
||||||
|
|||||||
@@ -458,7 +458,7 @@ public sealed class DynamicK3dSessionTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task UpdateOnAir_UsesPlayingSceneAndAppliesOnlyBeforeTransactionBackgroundSetup()
|
public async Task UpdateOnAir_ReusesRetainedSceneAndIgnoresInvalidPlayingSceneProxy()
|
||||||
{
|
{
|
||||||
using var scenes = TemporarySceneDirectory.Create(
|
using var scenes = TemporarySceneDirectory.Create(
|
||||||
"test-scene.t2s",
|
"test-scene.t2s",
|
||||||
@@ -484,7 +484,7 @@ public sealed class DynamicK3dSessionTests
|
|||||||
]));
|
]));
|
||||||
var log = new FakeComLog();
|
var log = new FakeComLog();
|
||||||
var scene = new FakeScene(log, "test-scene");
|
var scene = new FakeScene(log, "test-scene");
|
||||||
var player = new FakePlayer(log) { PlayingScene = scene };
|
var player = new FakePlayer(log) { PlayingScene = new object() };
|
||||||
var activator = new FakeActivator(log, new FakeEngine(log, player, scene));
|
var activator = new FakeActivator(log, new FakeEngine(log, player, scene));
|
||||||
await using var dispatcher = new StaDispatcher(capacity: 2);
|
await using var dispatcher = new StaDispatcher(capacity: 2);
|
||||||
var updateStart = 0;
|
var updateStart = 0;
|
||||||
@@ -514,7 +514,6 @@ public sealed class DynamicK3dSessionTests
|
|||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
"GetPlayingScene:10",
|
|
||||||
"UseBackground:0",
|
"UseBackground:0",
|
||||||
"SetBackgroundTexture:background.vrv",
|
"SetBackgroundTexture:background.vrv",
|
||||||
"SetBackgroundVideo:background.vrv:4:1",
|
"SetBackgroundVideo:background.vrv:4:1",
|
||||||
@@ -531,20 +530,274 @@ public sealed class DynamicK3dSessionTests
|
|||||||
"Play:10"
|
"Play:10"
|
||||||
},
|
},
|
||||||
updateCalls);
|
updateCalls);
|
||||||
|
Assert.Equal(1, updateCalls.Count(call => call == "Play:10"));
|
||||||
|
Assert.DoesNotContain("GetPlayingScene:10", log.Names);
|
||||||
Assert.DoesNotContain(updateCalls, call =>
|
Assert.DoesNotContain(updateCalls, call =>
|
||||||
call.StartsWith("LoadScene:", StringComparison.Ordinal) ||
|
call.StartsWith("LoadScene:", StringComparison.Ordinal) ||
|
||||||
call.StartsWith("SetSceneEffectType:", StringComparison.Ordinal));
|
call.StartsWith("SetSceneEffectType:", StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task UpdateOnAir_CallbackSession_TracksItsPlayUntilMatchingCallback()
|
public async Task UpdateOnAir_MismatchedRetainedSceneFailsBeforeAnySdkMutation()
|
||||||
|
{
|
||||||
|
using var scenes = TemporarySceneDirectory.Create(
|
||||||
|
"5001.t2s",
|
||||||
|
"5006.t2s",
|
||||||
|
"Video\\background.vrv");
|
||||||
|
var options = SceneOptions(scenes.Path, "5001", "5006");
|
||||||
|
var initialCue = SceneCue(options, "5001");
|
||||||
|
var mismatchedCue = options.ResolveCue(new PlayoutCue(
|
||||||
|
"5006.t2s",
|
||||||
|
"5006",
|
||||||
|
[new PlayoutField("headline", "must not be sent", true)],
|
||||||
|
Mutations:
|
||||||
|
[
|
||||||
|
new PlayoutUseBackground(false),
|
||||||
|
new PlayoutSetBackgroundTexture("Video/background.vrv")
|
||||||
|
]));
|
||||||
|
var log = new FakeComLog();
|
||||||
|
var retainedScene = new FakeScene(log, "5001");
|
||||||
|
var player = new FakePlayer(log) { PlayingScene = new object() };
|
||||||
|
var activator = new FakeActivator(
|
||||||
|
log,
|
||||||
|
new FakeEngine(log, player, retainedScene));
|
||||||
|
await using var dispatcher = new StaDispatcher(capacity: 2);
|
||||||
|
string[] updateCalls = [];
|
||||||
|
|
||||||
|
await dispatcher.InvokeAsync(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
using var session = new DynamicK3dSession(activator);
|
||||||
|
session.Connect(options);
|
||||||
|
session.Prepare(initialCue, options.LayoutIndex);
|
||||||
|
session.Play(options.LayoutIndex);
|
||||||
|
|
||||||
|
var updateStart = log.Names.Length;
|
||||||
|
Assert.Throws<InvalidOperationException>(
|
||||||
|
() => session.UpdateOnAir(mismatchedCue, options.LayoutIndex));
|
||||||
|
updateCalls = log.Names.Skip(updateStart).ToArray();
|
||||||
|
session.Disconnect();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
TimeSpan.FromSeconds(5),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Empty(updateCalls);
|
||||||
|
Assert.DoesNotContain("GetPlayingScene:10", log.Names);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateOnAir_WithoutRetainedCurrentSceneFailsBeforeAnySdkMutation()
|
||||||
|
{
|
||||||
|
using var scenes = TemporarySceneDirectory.Create("5001.t2s");
|
||||||
|
var options = SceneOptions(scenes.Path, "5001");
|
||||||
|
var cue = SceneCue(options, "5001");
|
||||||
|
var log = new FakeComLog();
|
||||||
|
var player = new FakePlayer(log) { PlayingScene = new object() };
|
||||||
|
var scene = new FakeScene(log, "5001");
|
||||||
|
var activator = new FakeActivator(log, new FakeEngine(log, player, scene));
|
||||||
|
await using var dispatcher = new StaDispatcher(capacity: 2);
|
||||||
|
string[] updateCalls = [];
|
||||||
|
|
||||||
|
await dispatcher.InvokeAsync(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
using var session = new DynamicK3dSession(activator);
|
||||||
|
session.Connect(options);
|
||||||
|
|
||||||
|
var updateStart = log.Names.Length;
|
||||||
|
Assert.Throws<InvalidOperationException>(
|
||||||
|
() => session.UpdateOnAir(cue, options.LayoutIndex));
|
||||||
|
updateCalls = log.Names.Skip(updateStart).ToArray();
|
||||||
|
session.Disconnect();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
TimeSpan.FromSeconds(5),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Empty(updateCalls);
|
||||||
|
Assert.DoesNotContain("GetPlayingScene:10", log.Names);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateOnAir_WithAnotherPreparedSceneFailsBeforeAnySdkMutation()
|
||||||
|
{
|
||||||
|
using var scenes = TemporarySceneDirectory.Create("5001.t2s", "5006.t2s");
|
||||||
|
var options = SceneOptions(scenes.Path, "5001", "5006");
|
||||||
|
var cue5001 = SceneCue(options, "5001");
|
||||||
|
var log = new FakeComLog();
|
||||||
|
var player = new FakePlayer(log) { PlayingScene = new object() };
|
||||||
|
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);
|
||||||
|
await using var dispatcher = new StaDispatcher(capacity: 2);
|
||||||
|
string[] updateCalls = [];
|
||||||
|
|
||||||
|
await dispatcher.InvokeAsync(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
using var session = new DynamicK3dSession(activator);
|
||||||
|
session.Connect(options);
|
||||||
|
session.Prepare(cue5001, options.LayoutIndex);
|
||||||
|
session.Play(options.LayoutIndex);
|
||||||
|
session.Prepare(SceneCue(options, "5006"), options.LayoutIndex);
|
||||||
|
|
||||||
|
var updateStart = log.Names.Length;
|
||||||
|
Assert.Throws<InvalidOperationException>(
|
||||||
|
() => session.UpdateOnAir(cue5001, options.LayoutIndex));
|
||||||
|
updateCalls = log.Names.Skip(updateStart).ToArray();
|
||||||
|
session.Disconnect();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
TimeSpan.FromSeconds(5),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Empty(updateCalls);
|
||||||
|
Assert.DoesNotContain("GetPlayingScene:10", log.Names);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateOnAir_WithPendingInitialPlayFailsBeforeAnySdkMutation()
|
||||||
{
|
{
|
||||||
using var scenes = TemporarySceneDirectory.Create("5001.t2s");
|
using var scenes = TemporarySceneDirectory.Create("5001.t2s");
|
||||||
var options = SceneOptions(scenes.Path, "5001");
|
var options = SceneOptions(scenes.Path, "5001");
|
||||||
var cue = SceneCue(options, "5001");
|
var cue = SceneCue(options, "5001");
|
||||||
var log = new FakeComLog();
|
var log = new FakeComLog();
|
||||||
var scene = new FakeScene(log, "5001");
|
var scene = new FakeScene(log, "5001");
|
||||||
var player = new FakePlayer(log) { PlayingScene = scene };
|
var player = new FakePlayer(log) { PlayingScene = new object() };
|
||||||
|
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);
|
||||||
|
string[] updateCalls = [];
|
||||||
|
|
||||||
|
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);
|
||||||
|
Assert.True(session.HasPendingPlayCallbacks);
|
||||||
|
|
||||||
|
var updateStart = log.Names.Length;
|
||||||
|
Assert.Throws<InvalidOperationException>(
|
||||||
|
() => session.UpdateOnAir(cue, options.LayoutIndex));
|
||||||
|
updateCalls = log.Names.Skip(updateStart).ToArray();
|
||||||
|
|
||||||
|
callbacks.Fire("OnScenePlayed", 1, 9, options.LayoutIndex);
|
||||||
|
session.ProcessPendingCallbacks(options.LayoutIndex);
|
||||||
|
Assert.False(session.HasPendingPlayCallbacks);
|
||||||
|
session.Disconnect();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
TimeSpan.FromSeconds(5),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Empty(updateCalls);
|
||||||
|
Assert.DoesNotContain("GetPlayingScene:10", log.Names);
|
||||||
|
Assert.Equal(1, log.Names.Count(name => name == "Release:5001"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateOnAir_TransactionFailureRollsBackAndRetainsSceneForStopAllCleanup()
|
||||||
|
{
|
||||||
|
using var scenes = TemporarySceneDirectory.Create("5001.t2s");
|
||||||
|
var options = SceneOptions(scenes.Path, "5001");
|
||||||
|
var initialCue = SceneCue(options, "5001");
|
||||||
|
var updateCue = options.ResolveCue(new PlayoutCue(
|
||||||
|
"5001.t2s",
|
||||||
|
"5001",
|
||||||
|
[new PlayoutField("headline", "refresh", true)],
|
||||||
|
Mutations: [new PlayoutUseBackground(false)]));
|
||||||
|
var log = new FakeComLog();
|
||||||
|
var scene = new FakeScene(log, "5001");
|
||||||
|
var player = new FakePlayer(log) { PlayingScene = new object() };
|
||||||
|
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(initialCue, options.LayoutIndex);
|
||||||
|
session.Play(options.LayoutIndex);
|
||||||
|
callbacks.Fire("OnScenePlayed", 1, 9, options.LayoutIndex);
|
||||||
|
session.ProcessPendingCallbacks(options.LayoutIndex);
|
||||||
|
|
||||||
|
scene.QueryVariablesFailure = new InvalidOperationException("fake refresh failure");
|
||||||
|
var updateStart = log.Names.Length;
|
||||||
|
Assert.Throws<InvalidOperationException>(
|
||||||
|
() => session.UpdateOnAir(updateCue, options.LayoutIndex));
|
||||||
|
var updateCalls = log.Names.Skip(updateStart).ToArray();
|
||||||
|
Assert.Equal(
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
"UseBackground:0",
|
||||||
|
"BeginTransaction",
|
||||||
|
"GetObject:headline",
|
||||||
|
"SetValue:headline:refresh",
|
||||||
|
"SetVisible:headline:1",
|
||||||
|
"QueryVariables",
|
||||||
|
"RollbackTransaction"
|
||||||
|
},
|
||||||
|
updateCalls);
|
||||||
|
Assert.DoesNotContain("Release:5001", log.Names);
|
||||||
|
Assert.DoesNotContain("Unload:5001", log.Names);
|
||||||
|
|
||||||
|
scene.QueryVariablesFailure = null;
|
||||||
|
session.TakeOut(options.LayoutIndex, PlayoutTakeOutScope.All);
|
||||||
|
callbacks.Fire("OnStopAll", 1);
|
||||||
|
Assert.Equal(
|
||||||
|
1,
|
||||||
|
session.ProcessPendingCallbacks(options.LayoutIndex).UnloadedSceneCount);
|
||||||
|
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 == "Release:5001"));
|
||||||
|
Assert.DoesNotContain("GetPlayingScene:10", log.Names);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateOnAir_CallbackSession_TracksPlayAndRetainsSceneUntilStopAll()
|
||||||
|
{
|
||||||
|
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 = new object() };
|
||||||
var engine = new FakeEngine(log, player, scene);
|
var engine = new FakeEngine(log, player, scene);
|
||||||
var activator = new FakeActivator(log, engine);
|
var activator = new FakeActivator(log, engine);
|
||||||
var callbacks = new RecordingEventHandlerFactory();
|
var callbacks = new RecordingEventHandlerFactory();
|
||||||
@@ -577,6 +830,8 @@ public sealed class DynamicK3dSessionTests
|
|||||||
|
|
||||||
session.UpdateOnAir(cue, options.LayoutIndex);
|
session.UpdateOnAir(cue, options.LayoutIndex);
|
||||||
Assert.True(session.HasPendingLifecycleCallbacks);
|
Assert.True(session.HasPendingLifecycleCallbacks);
|
||||||
|
Assert.DoesNotContain("Release:5001", log.Names);
|
||||||
|
Assert.DoesNotContain("Unload:5001", log.Names);
|
||||||
|
|
||||||
callbacks.Fire("OnScenePlayed", 1, 8, 10);
|
callbacks.Fire("OnScenePlayed", 1, 8, 10);
|
||||||
session.ProcessPendingCallbacks(options.LayoutIndex);
|
session.ProcessPendingCallbacks(options.LayoutIndex);
|
||||||
@@ -585,6 +840,13 @@ public sealed class DynamicK3dSessionTests
|
|||||||
callbacks.Fire("OnScenePlayed", 1, 9, 10);
|
callbacks.Fire("OnScenePlayed", 1, 9, 10);
|
||||||
session.ProcessPendingCallbacks(options.LayoutIndex);
|
session.ProcessPendingCallbacks(options.LayoutIndex);
|
||||||
Assert.False(session.HasPendingLifecycleCallbacks);
|
Assert.False(session.HasPendingLifecycleCallbacks);
|
||||||
|
|
||||||
|
session.TakeOut(options.LayoutIndex, PlayoutTakeOutScope.All);
|
||||||
|
callbacks.Fire("OnStopAll", 1);
|
||||||
|
Assert.Equal(
|
||||||
|
1,
|
||||||
|
session.ProcessPendingCallbacks(options.LayoutIndex).UnloadedSceneCount);
|
||||||
|
Assert.False(session.HasPendingLifecycleCallbacks);
|
||||||
session.Disconnect();
|
session.Disconnect();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@@ -592,8 +854,9 @@ public sealed class DynamicK3dSessionTests
|
|||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal(2, log.Names.Count(name => name == "Play:10"));
|
Assert.Equal(2, log.Names.Count(name => name == "Play:10"));
|
||||||
Assert.Equal(1, log.Names.Count(name => name == "GetPlayingScene:10"));
|
Assert.DoesNotContain("GetPlayingScene:10", log.Names);
|
||||||
Assert.DoesNotContain("Unload:5001", log.Names);
|
Assert.Equal(1, log.Names.Count(name => name == "Unload:5001"));
|
||||||
|
Assert.Equal(1, log.Names.Count(name => name == "Release:5001"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -1729,7 +1992,7 @@ public sealed class DynamicK3dSessionTests
|
|||||||
{
|
{
|
||||||
private readonly FakeComLog _log;
|
private readonly FakeComLog _log;
|
||||||
private readonly string? _name;
|
private readonly string? _name;
|
||||||
private readonly Exception? _queryVariablesFailure;
|
private Exception? _queryVariablesFailure;
|
||||||
private readonly Exception? _unloadFailure;
|
private readonly Exception? _unloadFailure;
|
||||||
|
|
||||||
public FakeScene(
|
public FakeScene(
|
||||||
@@ -1773,6 +2036,12 @@ public sealed class DynamicK3dSessionTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Exception? QueryVariablesFailure
|
||||||
|
{
|
||||||
|
get => _queryVariablesFailure;
|
||||||
|
set => _queryVariablesFailure = value;
|
||||||
|
}
|
||||||
|
|
||||||
public void Unload()
|
public void Unload()
|
||||||
{
|
{
|
||||||
_log.Add(_name is null ? "Unload" : $"Unload:{_name}");
|
_log.Add(_name is null ? "Unload" : $"Unload:{_name}");
|
||||||
|
|||||||
Reference in New Issue
Block a user