Move use throttling, appraisal identity, queued interactions, and exact post-arrival pickup state beneath RuntimeActionState. Keep App as the picker, movement, transport, and retained-presentation adapter while preserving retail send and UseDone ordering. Add reset, disposal, GUID-reuse, callback-reentrancy, and transport-failure coverage. Co-authored-by: Codex <noreply@openai.com>
188 lines
5.9 KiB
C#
188 lines
5.9 KiB
C#
namespace AcDream.App.Tests.Runtime;
|
|
|
|
public sealed class RuntimeInventoryOwnershipTests
|
|
{
|
|
[Fact]
|
|
public void ProductionConstructsOnlyTheRuntimeInventoryOwner()
|
|
{
|
|
string source = ReadSource("Rendering", "GameWindow.cs");
|
|
|
|
Assert.Contains(
|
|
"_runtimeInventory = new RuntimeInventoryState(_runtimeEntityObjects);",
|
|
source,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"new AcDream.Core.Items.ItemManaState",
|
|
source,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"new DesiredComponentSnapshotState",
|
|
source,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"new ShortcutSnapshotState",
|
|
source,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"new AcDream.Core.Items.ExternalContainerState",
|
|
source,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"DesiredComponentState",
|
|
source,
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void UiSessionAndShutdownBorrowTheExactRuntimeOwner()
|
|
{
|
|
string ui = ReadSource(
|
|
"Composition",
|
|
"InteractionRetainedUiComposition.cs");
|
|
string session = ReadSource("Net", "LiveSessionRuntimeFactory.cs");
|
|
string shutdown = ReadSource("Rendering", "GameWindowLifetime.cs");
|
|
string itemInteraction = ReadSource(
|
|
"UI",
|
|
"ItemInteractionController.cs");
|
|
|
|
Assert.Contains(
|
|
"d.Actions.Transactions",
|
|
ui,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"new InventoryTransactionState(d.Inventory.Objects)",
|
|
ui,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"new InventoryTransactionState",
|
|
itemInteraction,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"_ownsTransactions",
|
|
itemInteraction,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"RuntimeInteractionTransactionState runtimeTransactions,",
|
|
itemInteraction,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"OnShortcuts: _domain.Inventory.Shortcuts.Load",
|
|
session,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"_domain.Actions.Transactions.CompleteUse(error)",
|
|
session,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"_domain.Inventory.DesiredComponents",
|
|
session,
|
|
StringComparison.Ordinal);
|
|
AssertAppearsInOrder(
|
|
shutdown,
|
|
"\"runtime action state\"",
|
|
"\"runtime inventory state\"",
|
|
"\"runtime entity/object lifetime\"");
|
|
}
|
|
|
|
[Fact]
|
|
public void ToolbarBorrowsRuntimeShortcutManagerWithoutAProviderOrMirror()
|
|
{
|
|
string toolbar = ReadSource(
|
|
"UI",
|
|
"Layout",
|
|
"ToolbarController.cs");
|
|
string ui = ReadSource(
|
|
"Composition",
|
|
"InteractionRetainedUiComposition.cs");
|
|
|
|
Assert.Contains(
|
|
"private readonly ShortcutStore _store;",
|
|
toolbar,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"_store = shortcuts ?? throw",
|
|
toolbar,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"new ShortcutStore()",
|
|
toolbar,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"Func<IReadOnlyList<ShortcutEntry>>",
|
|
toolbar,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"_storeLoaded",
|
|
toolbar,
|
|
StringComparison.Ordinal);
|
|
Assert.Contains(
|
|
"d.Inventory.Shortcuts,",
|
|
ui,
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void SpellUiDoesNotApplyASecondLocalCommandMutation()
|
|
{
|
|
string spellbook = ReadSource(
|
|
"UI",
|
|
"Layout",
|
|
"SpellbookWindowController.cs");
|
|
string spellcasting = ReadSource(
|
|
"UI",
|
|
"Layout",
|
|
"SpellcastingUiController.cs");
|
|
|
|
Assert.DoesNotContain(
|
|
"_spellbook.SetSpellbookFilters(filters);",
|
|
spellbook,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"_spellbook.SetDesiredComponent(componentId, parsed);",
|
|
spellbook,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"_spellbook.SetFavorite(",
|
|
spellcasting,
|
|
StringComparison.Ordinal);
|
|
Assert.DoesNotContain(
|
|
"_spellbook.RemoveFavorite(",
|
|
spellcasting,
|
|
StringComparison.Ordinal);
|
|
}
|
|
|
|
private static string ReadSource(params string[] relative)
|
|
{
|
|
string root = FindRepositoryRoot();
|
|
return 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;
|
|
}
|
|
}
|
|
}
|