test(gates): sequence the two-client remote-shadow gate with harness signals
Imported from the codex worktree's uncommitted work
(C:/Users/erikn/.codex/worktrees/bd98/acdream on
codex/atmospheric-rendering-campaign), on top of its 8b7b601b.
The remote-player shadow gate ran two clients on fixed sleeps, so the
observer could move before the primary had taken its 'before' screenshot,
or the primary could take its 'after' shot before the observer had moved.
Now the harness publishes named signal files into each client's artifact
directory and the routes block on them:
- IRetailUiAutomationRuntime.TryIsAutomationSignalPublished, implemented
by WorldLifecycleAutomationController over <artifactDir>/signals/
<name>.signal (names validated by AutomationArtifactName, so no path
escape).
- 'wait signal <name> [timeoutMs]' in RetailUiAutomationScriptRunner.
- run-connected-render-pack-remote-player-gate.ps1 publishes
'primary-before' to the observer after the primary's before-shot
completes, then 'observer-moved' to the primary after the observer's
remote-observer-moved checkpoint.
- Both routes teleport with an explicit heading and wait 12 s to settle.
Build green; the three touched App test classes pass 58/58 including the
two new signal tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
8b7b601b32
commit
c51b07ef95
8 changed files with 189 additions and 10 deletions
|
|
@ -250,6 +250,8 @@ Remove-ConnectedGraphicalSessionConfig -State $state -Path $sessionConfigPath
|
|||
Assert.Contains("[int]$classes.RemotePlayers -lt 1", source, StringComparison.Ordinal);
|
||||
Assert.Contains("live: session failed:", source, StringComparison.Ordinal);
|
||||
Assert.Contains("DistinctAccountsConfigured", source, StringComparison.Ordinal);
|
||||
Assert.Contains("Publish-ClientSignal $observerRoot 'primary-before'", source, StringComparison.Ordinal);
|
||||
Assert.Contains("Publish-ClientSignal $primaryRoot 'observer-moved'", source, StringComparison.Ordinal);
|
||||
Assert.Contains("both clients entered world with the same character identity", source, StringComparison.Ordinal);
|
||||
Assert.Contains("Restore-ConnectedRenderPackGateEnvironment $primaryState", source, StringComparison.Ordinal);
|
||||
Assert.Contains("Restore-ConnectedRenderPackGateEnvironment $observerState", source, StringComparison.Ordinal);
|
||||
|
|
@ -267,9 +269,11 @@ Remove-ConnectedGraphicalSessionConfig -State $state -Path $sessionConfigPath
|
|||
observer,
|
||||
"command /teleloc 0x09040008",
|
||||
"wait materialized 1 90000",
|
||||
"checkpoint remote-observer-ready",
|
||||
"wait signal primary-before 120000",
|
||||
"input down MovementForward",
|
||||
"input up MovementForward",
|
||||
"checkpoint remote-observer");
|
||||
"checkpoint remote-observer-moved");
|
||||
|
||||
string primary = ReadTool("connected-render-pack-remote-primary.route.txt");
|
||||
AssertAppearsInOrder(
|
||||
|
|
@ -277,6 +281,7 @@ Remove-ConnectedGraphicalSessionConfig -State $state -Path $sessionConfigPath
|
|||
"command /teleloc 0x09040008",
|
||||
"wait materialized 1 90000",
|
||||
"screenshot remote-player-before 15000",
|
||||
"wait signal observer-moved 120000",
|
||||
"screenshot remote-player-after 15000",
|
||||
"checkpoint remote-primary");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -703,6 +703,48 @@ public sealed class WorldLifecycleAutomationControllerTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutomationSignalReadsOnlyValidatedNamesFromOwnedArtifactDirectory()
|
||||
{
|
||||
string directory = NewDirectory();
|
||||
var controller = new WorldLifecycleAutomationController(
|
||||
() => default,
|
||||
() => default,
|
||||
() => default,
|
||||
() => 0,
|
||||
_ => EmptyResources(),
|
||||
new FrameScreenshotController((_, _) => [], directory),
|
||||
directory);
|
||||
|
||||
try
|
||||
{
|
||||
Assert.True(controller.TryIsAutomationSignalPublished(
|
||||
"observer-moved", out bool missing, out string missingError),
|
||||
missingError);
|
||||
Assert.False(missing);
|
||||
|
||||
string signalDirectory = Path.Combine(directory, "signals");
|
||||
Directory.CreateDirectory(signalDirectory);
|
||||
File.WriteAllText(
|
||||
Path.Combine(signalDirectory, "observer-moved.signal"),
|
||||
"published");
|
||||
|
||||
Assert.True(controller.TryIsAutomationSignalPublished(
|
||||
"observer-moved", out bool published, out string publishedError),
|
||||
publishedError);
|
||||
Assert.True(published);
|
||||
Assert.False(controller.TryIsAutomationSignalPublished(
|
||||
"../outside", out bool invalid, out string invalidError));
|
||||
Assert.False(invalid);
|
||||
Assert.Contains("may contain only", invalidError);
|
||||
}
|
||||
finally
|
||||
{
|
||||
controller.Dispose();
|
||||
Directory.Delete(directory, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TransitionAutomationDelegatesPreserveExactDisableReenableAndResizeOwnership()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ public sealed class RetailUiAutomationProbeTests
|
|||
public List<string> Checkpoints { get; } = new();
|
||||
public HashSet<string> ScreenshotRequests { get; } = new();
|
||||
public HashSet<string> CompletedScreenshots { get; } = new();
|
||||
public HashSet<string> PublishedSignals { get; } = new();
|
||||
|
||||
public bool TryResetRenderPackPerformance(out string error)
|
||||
{
|
||||
|
|
@ -143,6 +144,16 @@ public sealed class RetailUiAutomationProbeTests
|
|||
}
|
||||
|
||||
public bool IsScreenshotComplete(string name) => CompletedScreenshots.Contains(name);
|
||||
|
||||
public bool TryIsAutomationSignalPublished(
|
||||
string name,
|
||||
out bool published,
|
||||
out string error)
|
||||
{
|
||||
published = PublishedSignals.Contains(name);
|
||||
error = string.Empty;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static (UiRoot root, UiItemList source, UiItemList target, SpyHandler handler, ClientObjectTable objects)
|
||||
|
|
@ -709,6 +720,55 @@ public sealed class RetailUiAutomationProbeTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptRunner_signalWaitBlocksUntilHarnessPublishesExactName()
|
||||
{
|
||||
var (root, _, _, _, objects) = RootWithTwoItemLists();
|
||||
var runtime = new FakeRuntime();
|
||||
var probe = new RetailUiAutomationProbe(root, objects);
|
||||
string path = Path.Combine(
|
||||
Path.GetTempPath(),
|
||||
Path.GetRandomFileName() + ".ui-probe.txt");
|
||||
File.WriteAllLines(path,
|
||||
[
|
||||
"wait signal observer-moved 1000",
|
||||
"input press CombatToggleCombat",
|
||||
]);
|
||||
var presses = new List<InputAction>();
|
||||
|
||||
try
|
||||
{
|
||||
using var runner = new RetailUiAutomationScriptRunner(
|
||||
probe,
|
||||
path,
|
||||
dumpOnStart: false,
|
||||
pressInput: action =>
|
||||
{
|
||||
presses.Add(action);
|
||||
return true;
|
||||
},
|
||||
runtime: runtime);
|
||||
|
||||
runner.Tick(0d);
|
||||
Assert.False(runner.Completed);
|
||||
Assert.Empty(presses);
|
||||
|
||||
runtime.PublishedSignals.Add("another-signal");
|
||||
runner.Tick(0.5d);
|
||||
Assert.False(runner.Completed);
|
||||
Assert.Empty(presses);
|
||||
|
||||
runtime.PublishedSignals.Add("observer-moved");
|
||||
runner.Tick(0.001d);
|
||||
Assert.True(runner.Completed);
|
||||
Assert.Equal([InputAction.CombatToggleCombat], presses);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptRunner_resetsThenWaitsForCompleteRenderPackEvidenceWindow()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue