acdream/tests/AcDream.App.Tests/Runtime/RuntimeActionOwnershipTests.cs
Erik ce41efb9e5 refactor(runtime): cut graphical host over to canonical root
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>
2026-07-26 19:06:09 +02:00

182 lines
6.5 KiB
C#

using System.Text.RegularExpressions;
namespace AcDream.App.Tests.Runtime;
public sealed class RuntimeActionOwnershipTests
{
[Fact]
public void ProductionConstructsOneCanonicalActionOwner()
{
string root = FindRepositoryRoot();
string gameWindow = ReadAppSource(
root,
"Rendering",
"GameWindow.cs");
string program = ReadAppSource(root, "Program.cs");
Assert.Contains(
"private readonly GameRuntime _runtime;",
gameWindow,
StringComparison.Ordinal);
Assert.Contains(
"_runtime = new GameRuntime(new GameRuntimeDependencies(",
gameWindow,
StringComparison.Ordinal);
Assert.Contains(
"_runtimeActions.Selection;",
gameWindow,
StringComparison.Ordinal);
Assert.Contains(
"_runtimeActions.Combat;",
gameWindow,
StringComparison.Ordinal);
Assert.Contains(
"window.Selection,",
program,
StringComparison.Ordinal);
string[] productionFiles = Directory
.EnumerateFiles(
Path.Combine(root, "src", "AcDream.App"),
"*.cs",
SearchOption.AllDirectories)
.Where(static path =>
!path.Contains(
$"{Path.DirectorySeparatorChar}Studio{Path.DirectorySeparatorChar}",
StringComparison.Ordinal))
.ToArray();
string production = string.Join(
"\n",
productionFiles.Select(File.ReadAllText));
Assert.Empty(Regex.Matches(
production,
@"\bnew\s+(?:AcDream\.Core\.Selection\.)?SelectionState\s*\("));
Assert.Empty(Regex.Matches(
production,
@"\bnew\s+(?:AcDream\.Core\.Combat\.)?CombatState\s*\("));
Assert.Empty(Regex.Matches(
production,
@"\bnew\s+(?:AcDream\.Runtime\.Gameplay\.)?InteractionState\s*\("));
Assert.Empty(Regex.Matches(
production,
@"\bnew\s+(?:AcDream\.Runtime\.Gameplay\.)?RuntimeInteractionTransactionState\s*\("));
Assert.Empty(Regex.Matches(
production,
@"\bnew\s+(?:AcDream\.Runtime\.Gameplay\.)?RuntimeCombatAttackState\s*\("));
Assert.Empty(Regex.Matches(
production,
@"\bnew\s+(?:AcDream\.Runtime\.Gameplay\.)?RuntimeCombatTargetState\s*\("));
Assert.Empty(Regex.Matches(
production,
@"\bnew\s+(?:AcDream\.Runtime\.Gameplay\.)?RuntimeCombatModeState\s*\("));
Assert.Empty(Regex.Matches(
production,
@"\bnew\s+(?:AcDream\.Runtime\.Gameplay\.)?RuntimeSpellCastState\s*\("));
Assert.Empty(Regex.Matches(
production,
@"\bnew\s+RuntimeActionState\s*\("));
}
[Fact]
public void UiSessionRuntimeAndShutdownBorrowTheExactActionChildren()
{
string root = FindRepositoryRoot();
string ui = ReadAppSource(
root,
"Composition",
"InteractionRetainedUiComposition.cs");
string session = ReadAppSource(
root,
"Composition",
"SessionPlayerComposition.cs");
string liveSession = ReadAppSource(
root,
"Net",
"LiveSessionRuntimeFactory.cs");
string commands = ReadAppSource(
root,
"Runtime",
"CurrentGameRuntimeCommandAdapter.cs");
string itemInteraction = ReadAppSource(
root,
"UI",
"ItemInteractionController.cs");
string shutdown = ReadAppSource(
root,
"Rendering",
"GameWindowLifetime.cs");
Assert.Contains("d.Actions.Interaction,", ui, StringComparison.Ordinal);
Assert.Contains("d.Actions.Transactions,", ui, StringComparison.Ordinal);
Assert.Contains("d.Actions.Selection", ui, StringComparison.Ordinal);
Assert.Contains("d.Actions.Combat", ui, StringComparison.Ordinal);
Assert.Contains(
"_dependencies.Actions.CombatAttack",
ui,
StringComparison.Ordinal);
Assert.Contains("d.Actions.SpellCast", ui, StringComparison.Ordinal);
Assert.Contains("d.Actions.Selection", session, StringComparison.Ordinal);
Assert.Contains("d.Actions.Combat", session, StringComparison.Ordinal);
Assert.Contains("_domain.Actions.Combat", liveSession, StringComparison.Ordinal);
Assert.Contains(
"_domain.Actions.Transactions.CompleteUse(error)",
liveSession,
StringComparison.Ordinal);
Assert.Contains("_actions.Selection", commands, StringComparison.Ordinal);
Assert.Contains(
"RuntimeInteractionTransactionState runtimeTransactions,",
itemInteraction,
StringComparison.Ordinal);
Assert.DoesNotContain(
"new InteractionState",
itemInteraction,
StringComparison.Ordinal);
Assert.Contains(
"new ResourceShutdownStage(\"game runtime root\"",
shutdown,
StringComparison.Ordinal);
Assert.DoesNotContain(
"RuntimeActionState Actions",
shutdown,
StringComparison.Ordinal);
Assert.False(File.Exists(Path.Combine(
root,
"src",
"AcDream.App",
"UI",
"InteractionState.cs")));
}
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.");
}
private static void AssertAppearsInOrder(
string source,
params string[] fragments)
{
int cursor = 0;
foreach (string fragment in fragments)
{
int index = source.IndexOf(
fragment,
cursor,
StringComparison.Ordinal);
Assert.True(index >= 0, $"Missing source fragment: {fragment}");
cursor = index + fragment.Length;
}
}
}