Move attack build/repeat state, combat-mode policy, authoritative auto-target transitions, and spell-cast intent beneath RuntimeActionState. Keep App as the input, world-query, DAT-policy, transport, and presentation adapter while preserving retail request and busy ordering. Add direct/graphical parity, reset, failure, and instance-isolation coverage. Co-authored-by: Codex <noreply@openai.com>
188 lines
6.7 KiB
C#
188 lines
6.7 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 RuntimeActionState _runtimeActions;",
|
|
gameWindow,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"_runtimeActions = new RuntimeActionState(",
|
|
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.Equal(
|
|
1,
|
|
Regex.Matches(
|
|
production,
|
|
@"\bnew\s+RuntimeActionState\s*\(").Count
|
|
+ Regex.Matches(
|
|
production,
|
|
@"RuntimeActionState\s+\w+\s*=\s*new\s*\(\s*\)").Count);
|
|
}
|
|
|
|
[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(
|
|
"\"runtime action state\"",
|
|
shutdown,
|
|
StringComparison.Ordinal);
|
|
AssertAppearsInOrder(
|
|
shutdown,
|
|
"\"runtime action state\"",
|
|
"\"runtime inventory state\"",
|
|
"\"runtime entity/object lifetime\"");
|
|
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;
|
|
}
|
|
}
|
|
}
|