feat(mosstank): add VTank-style automation PoC

This commit is contained in:
Erik 2026-08-27 18:57:21 +02:00
parent f6fe0f2a4f
commit 4e6e9bc9d9
212 changed files with 49462 additions and 416 deletions

View file

@ -1,5 +1,6 @@
using AcDream.App.Plugins;
using AcDream.App.UI;
using AcDream.Plugin.Abstractions;
namespace AcDream.App.Tests.Plugins;
@ -32,6 +33,8 @@ public class BufferedUiRegistryTests
var element = new UiPanel();
root.AddChild(element);
registry.CompleteMount(pending, root, element);
bool windowRemoved = false;
registry.CompleteWindowMount(pending, () => windowRemoved = true);
Assert.Contains(element, root.Children);
Assert.Equal(1, registry.RegistrationCount);
@ -40,5 +43,92 @@ public class BufferedUiRegistryTests
Assert.DoesNotContain(element, root.Children);
Assert.Equal(0, registry.RegistrationCount);
Assert.True(windowRemoved);
}
[Fact]
public void FirstClassPanelCarriesManifestOwnerAndStableWindowIdentity()
{
var registry = new BufferedUiRegistry();
var descriptor = new PluginPanelDescriptor("main", "MossTank")
{
IconText = "MT",
StartVisible = false,
};
registry.RegisterPanel(
new PluginUiOwner("acdream.mosstank", "MossTank"),
descriptor,
"mosstank.xml",
new object());
BufferedUiRegistry.Pending pending = Assert.Single(registry.Drain());
Assert.Equal("acdream.mosstank", pending.Owner.Id);
Assert.Same(descriptor, pending.Descriptor);
Assert.Equal("plugin:acdream.mosstank:main", pending.WindowName);
Assert.False(pending.Descriptor.StartVisible);
}
[Fact]
public void InlinePanelContentHasAnIndependentlyRemovableLifetime()
{
var registry = new BufferedUiRegistry();
IDisposable token = registry.RegisterPanelContent(
new PluginUiOwner("acdream.mosstank", "MossTank"),
new PluginPanelDescriptor("meta-status", "Status"),
"<panel w=\"100\" h=\"50\" />",
new object());
BufferedUiRegistry.Pending pending = Assert.Single(registry.Drain());
Assert.Equal("<panel w=\"100\" h=\"50\" />", pending.MarkupContent);
Assert.Equal("plugin:acdream.mosstank:meta-status", pending.WindowName);
token.Dispose();
Assert.Equal(0, registry.RegistrationCount);
}
[Fact]
public void LateWindowPublicationCleansUpAfterConcurrentDisposal()
{
var registry = new BufferedUiRegistry();
IDisposable token = registry.RegisterMarkupPanel("late.xml", new object());
BufferedUiRegistry.Pending pending = Assert.Single(registry.Drain());
token.Dispose();
bool cleaned = false;
registry.CompleteWindowMount(pending, () => cleaned = true);
Assert.True(cleaned);
Assert.Equal(0, registry.RegistrationCount);
}
[Fact]
public void ScopedViewsExposeOnlyTheirOwnNamedControls()
{
var registry = new BufferedUiRegistry();
var owner = new PluginUiOwner("acdream.mosstank", "MossTank");
registry.RegisterPanelContent(
owner,
new PluginPanelDescriptor("meta", "Status View"),
"<panel />",
new object());
BufferedUiRegistry.Pending pending = Assert.Single(registry.Drain());
var root = new UiRoot();
var panel = new UiPanel();
var button = new UiSimpleButton { Name = "Action", Text = "Old" };
panel.AddChild(button);
root.AddChild(panel);
registry.CompleteMount(pending, root, panel);
Assert.True(registry.ViewExists(owner, "Status View"));
Assert.True(registry.IsViewVisible(owner, "meta"));
Assert.True(registry.ControlExists(owner, "Status View", "Action"));
Assert.True(registry.SetControlLabel(owner, "Status View", "Action", "New"));
Assert.Equal("New", button.Text);
Assert.True(registry.SetControlVisible(
owner, "Status View", "Action", false));
Assert.False(button.Visible);
Assert.False(registry.ViewExists(
new PluginUiOwner("another.plugin", "Other"), "Status View"));
}
}