Make every App composition phase borrow one GameRuntime, retire the duplicate view/event adapters, and dispose the root only after its graphical borrowers release. This preserves synchronous UI commands while giving shutdown one exact ownership ledger. Co-authored-by: OpenAI Codex <codex@openai.com>
131 lines
4.4 KiB
C#
131 lines
4.4 KiB
C#
using System.Text.RegularExpressions;
|
|
|
|
namespace AcDream.App.Tests.Runtime;
|
|
|
|
public sealed class RuntimeMovementOwnershipTests
|
|
{
|
|
[Fact]
|
|
public void ProductionConstructsOneCanonicalRuntimeMovementOwner()
|
|
{
|
|
string root = FindRepositoryRoot();
|
|
string appRoot = Path.Combine(root, "src", "AcDream.App");
|
|
string runtimeRoot = Path.Combine(root, "src", "AcDream.Runtime");
|
|
string app = string.Join(
|
|
"\n",
|
|
Directory.EnumerateFiles(appRoot, "*.cs", SearchOption.AllDirectories)
|
|
.Select(File.ReadAllText));
|
|
string runtime = string.Join(
|
|
"\n",
|
|
Directory.EnumerateFiles(runtimeRoot, "*.cs", SearchOption.AllDirectories)
|
|
.Select(File.ReadAllText));
|
|
|
|
Assert.False(File.Exists(Path.Combine(
|
|
appRoot,
|
|
"Input",
|
|
"PlayerMovementController.cs")));
|
|
Assert.False(File.Exists(Path.Combine(
|
|
appRoot,
|
|
"Input",
|
|
"LocalPlayerOutboundController.cs")));
|
|
Assert.DoesNotContain(
|
|
"class PlayerMovementController",
|
|
app,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"class LocalPlayerOutboundController",
|
|
app,
|
|
StringComparison.Ordinal);
|
|
Assert.Empty(Regex.Matches(app, @"\bbool\s+_autoRunActive\b"));
|
|
Assert.Empty(Regex.Matches(
|
|
app,
|
|
@"RuntimeLocalPlayerMovementState\s+\w+\s*=\s*new\s*\(")
|
|
.Cast<Match>());
|
|
Assert.Single(Regex.Matches(
|
|
runtime,
|
|
@"new\s+RuntimeLocalPlayerMovementState\s*\(")
|
|
.Cast<Match>());
|
|
Assert.DoesNotContain(
|
|
"ACDREAM_RUN_SKILL",
|
|
runtime,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"ACDREAM_JUMP_SKILL",
|
|
runtime,
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void GraphicalInputRuntimeViewsAndShutdownBorrowTheExactOwner()
|
|
{
|
|
string root = FindRepositoryRoot();
|
|
string gameWindow = ReadAppSource(root, "Rendering", "GameWindow.cs");
|
|
string input = ReadAppSource(
|
|
root,
|
|
"Input",
|
|
"DispatcherMovementInputSource.cs");
|
|
string adapter = ReadAppSource(
|
|
root,
|
|
"Runtime",
|
|
"CurrentGameRuntimeAdapter.cs");
|
|
string commands = ReadAppSource(
|
|
root,
|
|
"Runtime",
|
|
"CurrentGameRuntimeCommandAdapter.cs");
|
|
string lifetime = ReadAppSource(
|
|
root,
|
|
"Rendering",
|
|
"GameWindowLifetime.cs");
|
|
|
|
Assert.Contains(
|
|
"RuntimeLocalPlayerMovementState _playerControllerSlot =>",
|
|
gameWindow,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"_runtime.MovementOwner;",
|
|
gameWindow,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"new AcDream.App.Input.DispatcherMovementInputSource(",
|
|
gameWindow,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"_playerControllerSlot,",
|
|
gameWindow,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"RuntimeLocalPlayerMovementState movement,",
|
|
input,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains("_movement.AutoRunActive", input, StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"private readonly GameRuntime _runtime;",
|
|
adapter,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains("_movement.Execute(command)", commands, StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"GameRuntime Runtime",
|
|
lifetime,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"\"game runtime root\"",
|
|
lifetime,
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
private static string ReadAppSource(string root, params string[] relative) =>
|
|
File.ReadAllText(Path.Combine(
|
|
[root, "src", "AcDream.App", .. relative]));
|
|
|
|
private static string FindRepositoryRoot()
|
|
{
|
|
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
|
while (current is not null)
|
|
{
|
|
if (File.Exists(Path.Combine(current.FullName, "AcDream.slnx")))
|
|
return current.FullName;
|
|
current = current.Parent;
|
|
}
|
|
|
|
throw new DirectoryNotFoundException("AcDream.slnx was not found.");
|
|
}
|
|
}
|